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

Merge pull request #11526 from grafana/11173_folder_admin

A folder admin should be able to add permissions for folder/its dashboards
Daniel Lee 7 лет назад
Родитель
Сommit
b3acbb9995

+ 11 - 3
pkg/api/api.go

@@ -149,8 +149,6 @@ func (hs *HTTPServer) registerRoutes() {
 
 		// team (admin permission required)
 		apiRoute.Group("/teams", func(teamsRoute RouteRegister) {
-			teamsRoute.Get("/:teamId", wrap(GetTeamByID))
-			teamsRoute.Get("/search", wrap(SearchTeams))
 			teamsRoute.Post("/", bind(m.CreateTeamCommand{}), wrap(CreateTeam))
 			teamsRoute.Put("/:teamId", bind(m.UpdateTeamCommand{}), wrap(UpdateTeam))
 			teamsRoute.Delete("/:teamId", wrap(DeleteTeamByID))
@@ -159,6 +157,12 @@ func (hs *HTTPServer) registerRoutes() {
 			teamsRoute.Delete("/:teamId/members/:userId", wrap(RemoveTeamMember))
 		}, reqOrgAdmin)
 
+		// team without requirement of user to be org admin
+		apiRoute.Group("/teams", func(teamsRoute RouteRegister) {
+			teamsRoute.Get("/:teamId", wrap(GetTeamByID))
+			teamsRoute.Get("/search", wrap(SearchTeams))
+		})
+
 		// org information available to all users.
 		apiRoute.Group("/org", func(orgRoute RouteRegister) {
 			orgRoute.Get("/", wrap(GetOrgCurrent))
@@ -170,7 +174,6 @@ func (hs *HTTPServer) registerRoutes() {
 			orgRoute.Put("/", bind(dtos.UpdateOrgForm{}), wrap(UpdateOrgCurrent))
 			orgRoute.Put("/address", bind(dtos.UpdateOrgAddressForm{}), wrap(UpdateOrgAddressCurrent))
 			orgRoute.Post("/users", quota("user"), bind(m.AddOrgUserCommand{}), wrap(AddOrgUserToCurrentOrg))
-			orgRoute.Get("/users", wrap(GetOrgUsersForCurrentOrg))
 			orgRoute.Patch("/users/:userId", bind(m.UpdateOrgUserCommand{}), wrap(UpdateOrgUserForCurrentOrg))
 			orgRoute.Delete("/users/:userId", wrap(RemoveOrgUserForCurrentOrg))
 
@@ -184,6 +187,11 @@ func (hs *HTTPServer) registerRoutes() {
 			orgRoute.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), wrap(UpdateOrgPreferences))
 		}, reqOrgAdmin)
 
+		// current org without requirement of user to be org admin
+		apiRoute.Group("/org", func(orgRoute RouteRegister) {
+			orgRoute.Get("/users", wrap(GetOrgUsersForCurrentOrg))
+		})
+
 		// create new org
 		apiRoute.Post("/orgs", quota("org"), bind(m.CreateOrgCommand{}), wrap(CreateOrg))
 

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

@@ -173,7 +173,7 @@ func (g *dashboardGuardianImpl) CheckPermissionBeforeUpdate(permission m.Permiss
 		return true, nil
 	}
 
-	return g.checkAcl(permission, acl)
+	return g.checkAcl(permission, existingPermissions)
 }
 
 // GetAcl returns dashboard acl

+ 617 - 664
pkg/services/guardian/guardian_test.go

@@ -2,710 +2,663 @@ package guardian
 
 import (
 	"fmt"
+	"runtime"
 	"testing"
 
-	"github.com/grafana/grafana/pkg/bus"
-
 	m "github.com/grafana/grafana/pkg/models"
 	. "github.com/smartystreets/goconvey/convey"
 )
 
-func TestGuardian(t *testing.T) {
-	Convey("Guardian permission tests", t, func() {
-		orgRoleScenario("Given user has admin org role", m.ROLE_ADMIN, func(sc *scenarioContext) {
-			canAdmin, _ := sc.g.CanAdmin()
-			canEdit, _ := sc.g.CanEdit()
-			canSave, _ := sc.g.CanSave()
-			canView, _ := sc.g.CanView()
-			So(canAdmin, ShouldBeTrue)
-			So(canEdit, ShouldBeTrue)
-			So(canSave, ShouldBeTrue)
-			So(canView, ShouldBeTrue)
-
-			Convey("When trying to update permissions", func() {
-				Convey("With duplicate user permissions should return error", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 1, UserId: 1, Permission: m.PERMISSION_VIEW},
-						{OrgId: 1, DashboardId: 1, UserId: 1, Permission: m.PERMISSION_ADMIN},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianPermissionExists)
-				})
-
-				Convey("With duplicate team permissions should return error", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 1, TeamId: 1, Permission: m.PERMISSION_VIEW},
-						{OrgId: 1, DashboardId: 1, TeamId: 1, Permission: m.PERMISSION_ADMIN},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianPermissionExists)
-				})
-
-				Convey("With duplicate everyone with editor role permission should return error", func() {
-					r := m.ROLE_EDITOR
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 1, Role: &r, Permission: m.PERMISSION_VIEW},
-						{OrgId: 1, DashboardId: 1, Role: &r, Permission: m.PERMISSION_ADMIN},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianPermissionExists)
-				})
-
-				Convey("With duplicate everyone with viewer role permission should return error", func() {
-					r := m.ROLE_VIEWER
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 1, Role: &r, Permission: m.PERMISSION_VIEW},
-						{OrgId: 1, DashboardId: 1, Role: &r, Permission: m.PERMISSION_ADMIN},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianPermissionExists)
-				})
-
-				Convey("With everyone with admin role permission should return error", func() {
-					r := m.ROLE_ADMIN
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 1, Role: &r, Permission: m.PERMISSION_ADMIN},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianPermissionExists)
-				})
-			})
-
-			Convey("Given default permissions", func() {
-				editor := m.ROLE_EDITOR
-				viewer := m.ROLE_VIEWER
-				existingPermissions := []*m.DashboardAclInfoDTO{
-					{OrgId: 1, DashboardId: -1, Role: &editor, Permission: m.PERMISSION_EDIT},
-					{OrgId: 1, DashboardId: -1, Role: &viewer, Permission: m.PERMISSION_VIEW},
-				}
-
-				bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
-					query.Result = existingPermissions
-					return nil
-				})
-
-				Convey("When trying to update dashboard permissions without everyone with role editor can edit should be allowed", func() {
-					r := m.ROLE_VIEWER
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 1, Role: &r, Permission: m.PERMISSION_VIEW},
-					}
-					ok, _ := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(ok, ShouldBeTrue)
-				})
-
-				Convey("When trying to update dashboard permissions without everyone with role viewer can view should be allowed", func() {
-					r := m.ROLE_EDITOR
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 1, Role: &r, Permission: m.PERMISSION_EDIT},
-					}
-					ok, _ := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(ok, ShouldBeTrue)
-				})
-			})
-
-			Convey("Given parent folder has user admin permission", func() {
-				existingPermissions := []*m.DashboardAclInfoDTO{
-					{OrgId: 1, DashboardId: 2, UserId: 1, Permission: m.PERMISSION_ADMIN},
-				}
-
-				bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
-					query.Result = existingPermissions
-					return nil
-				})
-
-				Convey("When trying to update dashboard permissions with admin user permission should return error", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, UserId: 1, Permission: m.PERMISSION_ADMIN},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianOverride)
-				})
-
-				Convey("When trying to update dashboard permissions with edit user permission should return error", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, UserId: 1, Permission: m.PERMISSION_EDIT},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianOverride)
-				})
-
-				Convey("When trying to update dashboard permissions with view user permission should return error", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, UserId: 1, Permission: m.PERMISSION_VIEW},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianOverride)
-				})
-			})
-
-			Convey("Given parent folder has user edit permission", func() {
-				existingPermissions := []*m.DashboardAclInfoDTO{
-					{OrgId: 1, DashboardId: 2, UserId: 1, Permission: m.PERMISSION_EDIT},
-				}
+var (
+	orgID              = int64(1)
+	defaultDashboardID = int64(-1)
+	dashboardID        = int64(1)
+	parentFolderID     = int64(2)
+	childDashboardID   = int64(3)
+	userID             = int64(1)
+	otherUserID        = int64(2)
+	teamID             = int64(1)
+	otherTeamID        = int64(2)
+	adminRole          = m.ROLE_ADMIN
+	editorRole         = m.ROLE_EDITOR
+	viewerRole         = m.ROLE_VIEWER
+)
 
-				bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
-					query.Result = existingPermissions
-					return nil
-				})
-
-				Convey("When trying to update dashboard permissions with admin user permission should be allowed", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, UserId: 1, Permission: m.PERMISSION_ADMIN},
-					}
-					ok, _ := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(ok, ShouldBeTrue)
-				})
-
-				Convey("When trying to update dashboard permissions with edit user permission should return error", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, UserId: 1, Permission: m.PERMISSION_EDIT},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianOverride)
-				})
-
-				Convey("When trying to update dashboard permissions with view user permission should return error", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, UserId: 1, Permission: m.PERMISSION_VIEW},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianOverride)
-				})
-			})
-
-			Convey("Given parent folder has user view permission", func() {
-				existingPermissions := []*m.DashboardAclInfoDTO{
-					{OrgId: 1, DashboardId: 2, UserId: 1, Permission: m.PERMISSION_VIEW},
-				}
+func TestGuardianAdmin(t *testing.T) {
+	Convey("Guardian admin org role tests", t, func() {
+		orgRoleScenario("Given user has admin org role", t, m.ROLE_ADMIN, func(sc *scenarioContext) {
+			// dashboard has default permissions
+			sc.defaultPermissionScenario(USER, m.PERMISSION_ADMIN, FULL_ACCESS)
+
+			// dashboard has user with permission
+			sc.dashboardPermissionScenario(USER, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.dashboardPermissionScenario(USER, m.PERMISSION_EDIT, FULL_ACCESS)
+			sc.dashboardPermissionScenario(USER, m.PERMISSION_VIEW, FULL_ACCESS)
+
+			// dashboard has team with permission
+			sc.dashboardPermissionScenario(TEAM, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.dashboardPermissionScenario(TEAM, m.PERMISSION_EDIT, FULL_ACCESS)
+			sc.dashboardPermissionScenario(TEAM, m.PERMISSION_VIEW, FULL_ACCESS)
+
+			// dashboard has editor role with permission
+			sc.dashboardPermissionScenario(EDITOR, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.dashboardPermissionScenario(EDITOR, m.PERMISSION_EDIT, FULL_ACCESS)
+			sc.dashboardPermissionScenario(EDITOR, m.PERMISSION_VIEW, FULL_ACCESS)
+
+			// dashboard has viewer role with permission
+			sc.dashboardPermissionScenario(VIEWER, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.dashboardPermissionScenario(VIEWER, m.PERMISSION_EDIT, FULL_ACCESS)
+			sc.dashboardPermissionScenario(VIEWER, m.PERMISSION_VIEW, FULL_ACCESS)
+
+			// parent folder has user with permission
+			sc.parentFolderPermissionScenario(USER, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.parentFolderPermissionScenario(USER, m.PERMISSION_EDIT, FULL_ACCESS)
+			sc.parentFolderPermissionScenario(USER, m.PERMISSION_VIEW, FULL_ACCESS)
+
+			// parent folder has team with permission
+			sc.parentFolderPermissionScenario(TEAM, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.parentFolderPermissionScenario(TEAM, m.PERMISSION_EDIT, FULL_ACCESS)
+			sc.parentFolderPermissionScenario(TEAM, m.PERMISSION_VIEW, FULL_ACCESS)
+
+			// parent folder has editor role with permission
+			sc.parentFolderPermissionScenario(EDITOR, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.parentFolderPermissionScenario(EDITOR, m.PERMISSION_EDIT, FULL_ACCESS)
+			sc.parentFolderPermissionScenario(EDITOR, m.PERMISSION_VIEW, FULL_ACCESS)
+
+			// parent folder has viweer role with permission
+			sc.parentFolderPermissionScenario(VIEWER, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.parentFolderPermissionScenario(VIEWER, m.PERMISSION_EDIT, FULL_ACCESS)
+			sc.parentFolderPermissionScenario(VIEWER, m.PERMISSION_VIEW, FULL_ACCESS)
+		})
+	})
+}
 
-				bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
-					query.Result = existingPermissions
-					return nil
-				})
-
-				Convey("When trying to update dashboard permissions with admin user permission should be allowed", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, UserId: 1, Permission: m.PERMISSION_ADMIN},
-					}
-					ok, _ := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(ok, ShouldBeTrue)
-				})
-
-				Convey("When trying to update dashboard permissions with edit user permission should be allowed", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, UserId: 1, Permission: m.PERMISSION_EDIT},
-					}
-					ok, _ := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(ok, ShouldBeTrue)
-				})
-
-				Convey("When trying to update dashboard permissions with view user permission should return error", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, UserId: 1, Permission: m.PERMISSION_VIEW},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianOverride)
-				})
-			})
-
-			Convey("Given parent folder has team admin permission", func() {
-				existingPermissions := []*m.DashboardAclInfoDTO{
-					{OrgId: 1, DashboardId: 2, TeamId: 1, Permission: m.PERMISSION_ADMIN},
-				}
+func TestGuardianEditor(t *testing.T) {
+	Convey("Guardian editor org role tests", t, func() {
+		orgRoleScenario("Given user has editor org role", t, m.ROLE_EDITOR, func(sc *scenarioContext) {
+			// dashboard has user with permission
+			sc.dashboardPermissionScenario(USER, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.dashboardPermissionScenario(USER, m.PERMISSION_EDIT, EDITOR_ACCESS)
+			sc.dashboardPermissionScenario(USER, m.PERMISSION_VIEW, CAN_VIEW)
+
+			// dashboard has team with permission
+			sc.dashboardPermissionScenario(TEAM, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.dashboardPermissionScenario(TEAM, m.PERMISSION_EDIT, EDITOR_ACCESS)
+			sc.dashboardPermissionScenario(TEAM, m.PERMISSION_VIEW, CAN_VIEW)
+
+			// dashboard has editor role with permission
+			sc.dashboardPermissionScenario(EDITOR, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.dashboardPermissionScenario(EDITOR, m.PERMISSION_EDIT, EDITOR_ACCESS)
+			sc.dashboardPermissionScenario(EDITOR, m.PERMISSION_VIEW, VIEWER_ACCESS)
+
+			// dashboard has viewer role with permission
+			sc.dashboardPermissionScenario(VIEWER, m.PERMISSION_ADMIN, NO_ACCESS)
+			sc.dashboardPermissionScenario(VIEWER, m.PERMISSION_EDIT, NO_ACCESS)
+			sc.dashboardPermissionScenario(VIEWER, m.PERMISSION_VIEW, NO_ACCESS)
+
+			// parent folder has user with permission
+			sc.parentFolderPermissionScenario(USER, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.parentFolderPermissionScenario(USER, m.PERMISSION_EDIT, EDITOR_ACCESS)
+			sc.parentFolderPermissionScenario(USER, m.PERMISSION_VIEW, VIEWER_ACCESS)
+
+			// parent folder has team with permission
+			sc.parentFolderPermissionScenario(TEAM, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.parentFolderPermissionScenario(TEAM, m.PERMISSION_EDIT, EDITOR_ACCESS)
+			sc.parentFolderPermissionScenario(TEAM, m.PERMISSION_VIEW, VIEWER_ACCESS)
+
+			// parent folder has editor role with permission
+			sc.parentFolderPermissionScenario(EDITOR, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.parentFolderPermissionScenario(EDITOR, m.PERMISSION_EDIT, EDITOR_ACCESS)
+			sc.parentFolderPermissionScenario(EDITOR, m.PERMISSION_VIEW, VIEWER_ACCESS)
+
+			// parent folder has viweer role with permission
+			sc.parentFolderPermissionScenario(VIEWER, m.PERMISSION_ADMIN, NO_ACCESS)
+			sc.parentFolderPermissionScenario(VIEWER, m.PERMISSION_EDIT, NO_ACCESS)
+			sc.parentFolderPermissionScenario(VIEWER, m.PERMISSION_VIEW, NO_ACCESS)
+		})
+	})
+}
 
-				bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
-					query.Result = existingPermissions
-					return nil
-				})
-
-				Convey("When trying to update dashboard permissions with admin team permission should return error", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, TeamId: 1, Permission: m.PERMISSION_ADMIN},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianOverride)
-				})
-
-				Convey("When trying to update dashboard permissions with edit team permission should return error", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, TeamId: 1, Permission: m.PERMISSION_EDIT},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianOverride)
-				})
-
-				Convey("When trying to update dashboard permissions with view team permission should return error", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, TeamId: 1, Permission: m.PERMISSION_VIEW},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianOverride)
-				})
-			})
-
-			Convey("Given parent folder has team edit permission", func() {
-				existingPermissions := []*m.DashboardAclInfoDTO{
-					{OrgId: 1, DashboardId: 2, TeamId: 1, Permission: m.PERMISSION_EDIT},
-				}
+func TestGuardianViewer(t *testing.T) {
+	Convey("Guardian viewer org role tests", t, func() {
+		orgRoleScenario("Given user has viewer org role", t, m.ROLE_VIEWER, func(sc *scenarioContext) {
+			// dashboard has user with permission
+			sc.dashboardPermissionScenario(USER, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.dashboardPermissionScenario(USER, m.PERMISSION_EDIT, EDITOR_ACCESS)
+			sc.dashboardPermissionScenario(USER, m.PERMISSION_VIEW, VIEWER_ACCESS)
+
+			// dashboard has team with permission
+			sc.dashboardPermissionScenario(TEAM, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.dashboardPermissionScenario(TEAM, m.PERMISSION_EDIT, EDITOR_ACCESS)
+			sc.dashboardPermissionScenario(TEAM, m.PERMISSION_VIEW, VIEWER_ACCESS)
+
+			// dashboard has editor role with permission
+			sc.dashboardPermissionScenario(EDITOR, m.PERMISSION_ADMIN, NO_ACCESS)
+			sc.dashboardPermissionScenario(EDITOR, m.PERMISSION_EDIT, NO_ACCESS)
+			sc.dashboardPermissionScenario(EDITOR, m.PERMISSION_VIEW, NO_ACCESS)
+
+			// dashboard has viewer role with permission
+			sc.dashboardPermissionScenario(VIEWER, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.dashboardPermissionScenario(VIEWER, m.PERMISSION_EDIT, EDITOR_ACCESS)
+			sc.dashboardPermissionScenario(VIEWER, m.PERMISSION_VIEW, VIEWER_ACCESS)
+
+			// parent folder has user with permission
+			sc.parentFolderPermissionScenario(USER, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.parentFolderPermissionScenario(USER, m.PERMISSION_EDIT, EDITOR_ACCESS)
+			sc.parentFolderPermissionScenario(USER, m.PERMISSION_VIEW, VIEWER_ACCESS)
+
+			// parent folder has team with permission
+			sc.parentFolderPermissionScenario(TEAM, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.parentFolderPermissionScenario(TEAM, m.PERMISSION_EDIT, EDITOR_ACCESS)
+			sc.parentFolderPermissionScenario(TEAM, m.PERMISSION_VIEW, VIEWER_ACCESS)
+
+			// parent folder has editor role with permission
+			sc.parentFolderPermissionScenario(EDITOR, m.PERMISSION_ADMIN, NO_ACCESS)
+			sc.parentFolderPermissionScenario(EDITOR, m.PERMISSION_EDIT, NO_ACCESS)
+			sc.parentFolderPermissionScenario(EDITOR, m.PERMISSION_VIEW, NO_ACCESS)
+
+			// parent folder has viweer role with permission
+			sc.parentFolderPermissionScenario(VIEWER, m.PERMISSION_ADMIN, FULL_ACCESS)
+			sc.parentFolderPermissionScenario(VIEWER, m.PERMISSION_EDIT, EDITOR_ACCESS)
+			sc.parentFolderPermissionScenario(VIEWER, m.PERMISSION_VIEW, VIEWER_ACCESS)
+		})
+	})
+}
 
-				bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
-					query.Result = existingPermissions
-					return nil
-				})
-
-				Convey("When trying to update dashboard permissions with admin team permission should be allowed", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, TeamId: 1, Permission: m.PERMISSION_ADMIN},
-					}
-					ok, _ := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(ok, ShouldBeTrue)
-				})
-
-				Convey("When trying to update dashboard permissions with edit team permission should return error", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, TeamId: 1, Permission: m.PERMISSION_EDIT},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianOverride)
-				})
-
-				Convey("When trying to update dashboard permissions with view team permission should return error", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, TeamId: 1, Permission: m.PERMISSION_VIEW},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianOverride)
-				})
-			})
-
-			Convey("Given parent folder has team view permission", func() {
-				existingPermissions := []*m.DashboardAclInfoDTO{
-					{OrgId: 1, DashboardId: 2, TeamId: 1, Permission: m.PERMISSION_VIEW},
-				}
+func (sc *scenarioContext) defaultPermissionScenario(pt permissionType, permission m.PermissionType, flag permissionFlags) {
+	_, callerFile, callerLine, _ := runtime.Caller(1)
+	sc.callerFile = callerFile
+	sc.callerLine = callerLine
+	existingPermissions := []*m.DashboardAclInfoDTO{
+		toDto(newEditorRolePermission(defaultDashboardID, m.PERMISSION_EDIT)),
+		toDto(newViewerRolePermission(defaultDashboardID, m.PERMISSION_VIEW)),
+	}
 
-				bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
-					query.Result = existingPermissions
-					return nil
-				})
-
-				Convey("When trying to update dashboard permissions with admin team permission should be allowed", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, TeamId: 1, Permission: m.PERMISSION_ADMIN},
-					}
-					ok, _ := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(ok, ShouldBeTrue)
-				})
-
-				Convey("When trying to update dashboard permissions with edit team permission should be allowed", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, TeamId: 1, Permission: m.PERMISSION_EDIT},
-					}
-					ok, _ := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(ok, ShouldBeTrue)
-				})
-
-				Convey("When trying to update dashboard permissions with view team permission should return error", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, TeamId: 1, Permission: m.PERMISSION_VIEW},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianOverride)
-				})
-			})
-
-			Convey("Given parent folder has editor role with edit permission", func() {
-				r := m.ROLE_EDITOR
-				existingPermissions := []*m.DashboardAclInfoDTO{
-					{OrgId: 1, DashboardId: 2, Role: &r, Permission: m.PERMISSION_EDIT},
-				}
+	permissionScenario("and existing permissions is the default permissions (everyone with editor role can edit, everyone with viewer role can view)", dashboardID, sc, existingPermissions, func(sc *scenarioContext) {
+		sc.expectedFlags = flag
+		sc.verifyExpectedPermissionsFlags()
+		sc.verifyDuplicatePermissionsShouldNotBeAllowed()
+		sc.verifyUpdateDashboardPermissionsShouldBeAllowed(pt)
+		sc.verifyUpdateDashboardPermissionsShouldNotBeAllowed(pt)
+	})
+}
 
-				bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
-					query.Result = existingPermissions
-					return nil
-				})
-
-				Convey("When trying to update dashboard permissions with everyone with editor role can admin permission should be allowed", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, Role: &r, Permission: m.PERMISSION_ADMIN},
-					}
-					ok, _ := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(ok, ShouldBeTrue)
-				})
-
-				Convey("When trying to update dashboard permissions with everyone with editor role can edit permission should return error", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, Role: &r, Permission: m.PERMISSION_EDIT},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianOverride)
-				})
-
-				Convey("When trying to update dashboard permissions with everyone with editor role can view permission should return error", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, Role: &r, Permission: m.PERMISSION_VIEW},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianOverride)
-				})
-			})
-
-			Convey("Given parent folder has editor role with view permission", func() {
-				r := m.ROLE_EDITOR
-				existingPermissions := []*m.DashboardAclInfoDTO{
-					{OrgId: 1, DashboardId: 2, Role: &r, Permission: m.PERMISSION_VIEW},
-				}
+func (sc *scenarioContext) dashboardPermissionScenario(pt permissionType, permission m.PermissionType, flag permissionFlags) {
+	_, callerFile, callerLine, _ := runtime.Caller(1)
+	sc.callerFile = callerFile
+	sc.callerLine = callerLine
+	var existingPermissions []*m.DashboardAclInfoDTO
+
+	switch pt {
+	case USER:
+		existingPermissions = []*m.DashboardAclInfoDTO{{OrgId: orgID, DashboardId: dashboardID, UserId: userID, Permission: permission}}
+	case TEAM:
+		existingPermissions = []*m.DashboardAclInfoDTO{{OrgId: orgID, DashboardId: dashboardID, TeamId: teamID, Permission: permission}}
+	case EDITOR:
+		existingPermissions = []*m.DashboardAclInfoDTO{{OrgId: orgID, DashboardId: dashboardID, Role: &editorRole, Permission: permission}}
+	case VIEWER:
+		existingPermissions = []*m.DashboardAclInfoDTO{{OrgId: orgID, DashboardId: dashboardID, Role: &viewerRole, Permission: permission}}
+	}
 
-				bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
-					query.Result = existingPermissions
-					return nil
-				})
-
-				Convey("When trying to update dashboard permissions with everyone with viewer role can admin permission should be allowed", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, Role: &r, Permission: m.PERMISSION_ADMIN},
-					}
-					ok, _ := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(ok, ShouldBeTrue)
-				})
-
-				Convey("When trying to update dashboard permissions with everyone with viewer role can edit permission should be allowed", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, Role: &r, Permission: m.PERMISSION_EDIT},
-					}
-					ok, _ := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(ok, ShouldBeTrue)
-				})
-
-				Convey("When trying to update dashboard permissions with everyone with viewer role can view permission should return error", func() {
-					p := []*m.DashboardAcl{
-						{OrgId: 1, DashboardId: 3, Role: &r, Permission: m.PERMISSION_VIEW},
-					}
-					_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-					So(err, ShouldEqual, ErrGuardianOverride)
-				})
-			})
-		})
+	permissionScenario(fmt.Sprintf("and %s has permission to %s dashboard", pt.String(), permission.String()), dashboardID, sc, existingPermissions, func(sc *scenarioContext) {
+		sc.expectedFlags = flag
+		sc.verifyExpectedPermissionsFlags()
+		sc.verifyDuplicatePermissionsShouldNotBeAllowed()
+		sc.verifyUpdateDashboardPermissionsShouldBeAllowed(pt)
+		sc.verifyUpdateDashboardPermissionsShouldNotBeAllowed(pt)
+	})
+}
 
-		orgRoleScenario("Given user has editor org role", m.ROLE_EDITOR, func(sc *scenarioContext) {
-			everyoneWithRoleScenario(m.ROLE_EDITOR, m.PERMISSION_ADMIN, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeTrue)
-				So(canEdit, ShouldBeTrue)
-				So(canSave, ShouldBeTrue)
-				So(canView, ShouldBeTrue)
-			})
-
-			everyoneWithRoleScenario(m.ROLE_EDITOR, m.PERMISSION_EDIT, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeFalse)
-				So(canEdit, ShouldBeTrue)
-				So(canSave, ShouldBeTrue)
-				So(canView, ShouldBeTrue)
-			})
-
-			everyoneWithRoleScenario(m.ROLE_EDITOR, m.PERMISSION_VIEW, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeFalse)
-				So(canEdit, ShouldBeFalse)
-				So(canSave, ShouldBeFalse)
-				So(canView, ShouldBeTrue)
-			})
-
-			everyoneWithRoleScenario(m.ROLE_VIEWER, m.PERMISSION_ADMIN, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeFalse)
-				So(canEdit, ShouldBeFalse)
-				So(canSave, ShouldBeFalse)
-				So(canView, ShouldBeFalse)
-			})
-
-			everyoneWithRoleScenario(m.ROLE_VIEWER, m.PERMISSION_EDIT, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeFalse)
-				So(canEdit, ShouldBeFalse)
-				So(canSave, ShouldBeFalse)
-				So(canView, ShouldBeFalse)
-			})
-
-			everyoneWithRoleScenario(m.ROLE_VIEWER, m.PERMISSION_VIEW, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeFalse)
-				So(canEdit, ShouldBeFalse)
-				So(canSave, ShouldBeFalse)
-				So(canView, ShouldBeFalse)
-			})
-
-			userWithPermissionScenario(m.PERMISSION_ADMIN, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeTrue)
-				So(canEdit, ShouldBeTrue)
-				So(canSave, ShouldBeTrue)
-				So(canView, ShouldBeTrue)
-			})
-
-			userWithPermissionScenario(m.PERMISSION_EDIT, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeFalse)
-				So(canEdit, ShouldBeTrue)
-				So(canSave, ShouldBeTrue)
-				So(canView, ShouldBeTrue)
-			})
-
-			userWithPermissionScenario(m.PERMISSION_VIEW, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeFalse)
-				So(canEdit, ShouldBeFalse)
-				So(canSave, ShouldBeFalse)
-				So(canView, ShouldBeTrue)
-			})
-
-			teamWithPermissionScenario(m.PERMISSION_ADMIN, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeTrue)
-				So(canEdit, ShouldBeTrue)
-				So(canSave, ShouldBeTrue)
-				So(canView, ShouldBeTrue)
-			})
-
-			teamWithPermissionScenario(m.PERMISSION_EDIT, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeFalse)
-				So(canEdit, ShouldBeTrue)
-				So(canSave, ShouldBeTrue)
-				So(canView, ShouldBeTrue)
-			})
-
-			teamWithPermissionScenario(m.PERMISSION_VIEW, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeFalse)
-				So(canEdit, ShouldBeFalse)
-				So(canSave, ShouldBeFalse)
-				So(canView, ShouldBeTrue)
-			})
-
-			Convey("When trying to update permissions should return false", func() {
-				p := []*m.DashboardAcl{
-					{OrgId: 1, DashboardId: 1, UserId: 1, Permission: m.PERMISSION_VIEW},
-					{OrgId: 1, DashboardId: 1, UserId: 1, Permission: m.PERMISSION_ADMIN},
-				}
-				ok, _ := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-				So(ok, ShouldBeFalse)
-			})
-		})
+func (sc *scenarioContext) parentFolderPermissionScenario(pt permissionType, permission m.PermissionType, flag permissionFlags) {
+	_, callerFile, callerLine, _ := runtime.Caller(1)
+	sc.callerFile = callerFile
+	sc.callerLine = callerLine
+	var folderPermissionList []*m.DashboardAclInfoDTO
+
+	switch pt {
+	case USER:
+		folderPermissionList = []*m.DashboardAclInfoDTO{{OrgId: orgID, DashboardId: parentFolderID, UserId: userID, Permission: permission}}
+	case TEAM:
+		folderPermissionList = []*m.DashboardAclInfoDTO{{OrgId: orgID, DashboardId: parentFolderID, TeamId: teamID, Permission: permission}}
+	case EDITOR:
+		folderPermissionList = []*m.DashboardAclInfoDTO{{OrgId: orgID, DashboardId: parentFolderID, Role: &editorRole, Permission: permission}}
+	case VIEWER:
+		folderPermissionList = []*m.DashboardAclInfoDTO{{OrgId: orgID, DashboardId: parentFolderID, Role: &viewerRole, Permission: permission}}
+	}
 
-		orgRoleScenario("Given user has viewer org role", m.ROLE_VIEWER, func(sc *scenarioContext) {
-			everyoneWithRoleScenario(m.ROLE_EDITOR, m.PERMISSION_ADMIN, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeFalse)
-				So(canEdit, ShouldBeFalse)
-				So(canSave, ShouldBeFalse)
-				So(canView, ShouldBeFalse)
-			})
-
-			everyoneWithRoleScenario(m.ROLE_EDITOR, m.PERMISSION_EDIT, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeFalse)
-				So(canEdit, ShouldBeFalse)
-				So(canSave, ShouldBeFalse)
-				So(canView, ShouldBeFalse)
-			})
-
-			everyoneWithRoleScenario(m.ROLE_EDITOR, m.PERMISSION_VIEW, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeFalse)
-				So(canEdit, ShouldBeFalse)
-				So(canSave, ShouldBeFalse)
-				So(canView, ShouldBeFalse)
-			})
-
-			everyoneWithRoleScenario(m.ROLE_VIEWER, m.PERMISSION_ADMIN, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeTrue)
-				So(canEdit, ShouldBeTrue)
-				So(canSave, ShouldBeTrue)
-				So(canView, ShouldBeTrue)
-			})
-
-			everyoneWithRoleScenario(m.ROLE_VIEWER, m.PERMISSION_EDIT, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeFalse)
-				So(canEdit, ShouldBeTrue)
-				So(canSave, ShouldBeTrue)
-				So(canView, ShouldBeTrue)
-			})
-
-			everyoneWithRoleScenario(m.ROLE_VIEWER, m.PERMISSION_VIEW, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeFalse)
-				So(canEdit, ShouldBeFalse)
-				So(canSave, ShouldBeFalse)
-				So(canView, ShouldBeTrue)
-			})
-
-			userWithPermissionScenario(m.PERMISSION_ADMIN, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeTrue)
-				So(canEdit, ShouldBeTrue)
-				So(canSave, ShouldBeTrue)
-				So(canView, ShouldBeTrue)
-			})
-
-			userWithPermissionScenario(m.PERMISSION_EDIT, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeFalse)
-				So(canEdit, ShouldBeTrue)
-				So(canSave, ShouldBeTrue)
-				So(canView, ShouldBeTrue)
-			})
-
-			userWithPermissionScenario(m.PERMISSION_VIEW, sc, func(sc *scenarioContext) {
-				canAdmin, _ := sc.g.CanAdmin()
-				canEdit, _ := sc.g.CanEdit()
-				canSave, _ := sc.g.CanSave()
-				canView, _ := sc.g.CanView()
-				So(canAdmin, ShouldBeFalse)
-				So(canEdit, ShouldBeFalse)
-				So(canSave, ShouldBeFalse)
-				So(canView, ShouldBeTrue)
-			})
-
-			Convey("When trying to update permissions should return false", func() {
-				p := []*m.DashboardAcl{
-					{OrgId: 1, DashboardId: 1, UserId: 1, Permission: m.PERMISSION_VIEW},
-					{OrgId: 1, DashboardId: 1, UserId: 1, Permission: m.PERMISSION_ADMIN},
-				}
-				ok, _ := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
-				So(ok, ShouldBeFalse)
-			})
-		})
+	permissionScenario(fmt.Sprintf("and parent folder has %s with permission to %s", pt.String(), permission.String()), childDashboardID, sc, folderPermissionList, func(sc *scenarioContext) {
+		sc.expectedFlags = flag
+		sc.verifyExpectedPermissionsFlags()
+		sc.verifyDuplicatePermissionsShouldNotBeAllowed()
+		sc.verifyUpdateChildDashboardPermissionsShouldBeAllowed(pt, permission)
+		sc.verifyUpdateChildDashboardPermissionsShouldNotBeAllowed(pt, permission)
+		sc.verifyUpdateChildDashboardPermissionsWithOverrideShouldBeAllowed(pt, permission)
+		sc.verifyUpdateChildDashboardPermissionsWithOverrideShouldNotBeAllowed(pt, permission)
 	})
 }
 
-type scenarioContext struct {
-	g DashboardGuardian
-}
+func (sc *scenarioContext) verifyExpectedPermissionsFlags() {
+	canAdmin, _ := sc.g.CanAdmin()
+	canEdit, _ := sc.g.CanEdit()
+	canSave, _ := sc.g.CanSave()
+	canView, _ := sc.g.CanView()
 
-type scenarioFunc func(c *scenarioContext)
+	tc := fmt.Sprintf("should have permissions to %s", sc.expectedFlags.String())
+	Convey(tc, func() {
+		var actualFlag permissionFlags
 
-func orgRoleScenario(desc string, role m.RoleType, fn scenarioFunc) {
-	user := &m.SignedInUser{
-		UserId:  1,
-		OrgId:   1,
-		OrgRole: role,
-	}
-	guard := New(1, 1, user)
-	sc := &scenarioContext{
-		g: guard,
-	}
+		if canAdmin {
+			actualFlag |= CAN_ADMIN
+		}
+
+		if canEdit {
+			actualFlag |= CAN_EDIT
+		}
+
+		if canSave {
+			actualFlag |= CAN_SAVE
+		}
+
+		if canView {
+			actualFlag |= CAN_VIEW
+		}
+
+		if actualFlag.noAccess() {
+			actualFlag = NO_ACCESS
+		}
+
+		if sc.expectedFlags&actualFlag != sc.expectedFlags {
+			sc.reportFailure(tc, sc.expectedFlags.String(), actualFlag.String())
+		}
 
-	Convey(desc, func() {
-		fn(sc)
+		sc.reportSuccess()
 	})
 }
 
-func permissionScenario(desc string, sc *scenarioContext, permissions []*m.DashboardAclInfoDTO, fn scenarioFunc) {
-	bus.ClearBusHandlers()
+func (sc *scenarioContext) verifyDuplicatePermissionsShouldNotBeAllowed() {
+	if !sc.expectedFlags.canAdmin() {
+		return
+	}
+
+	tc := "When updating dashboard permissions with duplicate permission for user should not be allowed"
+	Convey(tc, func() {
+		p := []*m.DashboardAcl{
+			newDefaultUserPermission(dashboardID, m.PERMISSION_VIEW),
+			newDefaultUserPermission(dashboardID, m.PERMISSION_ADMIN),
+		}
+		sc.updatePermissions = p
+		_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
 
-	bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
-		query.Result = permissions
-		return nil
+		if err != ErrGuardianPermissionExists {
+			sc.reportFailure(tc, ErrGuardianPermissionExists, err)
+		}
+		sc.reportSuccess()
 	})
 
-	teams := []*m.Team{}
+	tc = "When updating dashboard permissions with duplicate permission for team should not be allowed"
+	Convey(tc, func() {
+		p := []*m.DashboardAcl{
+			newDefaultTeamPermission(dashboardID, m.PERMISSION_VIEW),
+			newDefaultTeamPermission(dashboardID, m.PERMISSION_ADMIN),
+		}
+		sc.updatePermissions = p
+		_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
 
-	for _, p := range permissions {
-		if p.TeamId > 0 {
-			teams = append(teams, &m.Team{Id: p.TeamId})
+		if err != ErrGuardianPermissionExists {
+			sc.reportFailure(tc, ErrGuardianPermissionExists, err)
 		}
-	}
+		sc.reportSuccess()
+	})
+
+	tc = "When updating dashboard permissions with duplicate permission for editor role should not be allowed"
+	Convey(tc, func() {
+		p := []*m.DashboardAcl{
+			newEditorRolePermission(dashboardID, m.PERMISSION_VIEW),
+			newEditorRolePermission(dashboardID, m.PERMISSION_ADMIN),
+		}
+		sc.updatePermissions = p
+		_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
+
+		if err != ErrGuardianPermissionExists {
+			sc.reportFailure(tc, ErrGuardianPermissionExists, err)
+		}
+		sc.reportSuccess()
+	})
 
-	bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
-		query.Result = teams
-		return nil
+	tc = "When updating dashboard permissions with duplicate permission for viewer role should not be allowed"
+	Convey(tc, func() {
+		p := []*m.DashboardAcl{
+			newViewerRolePermission(dashboardID, m.PERMISSION_VIEW),
+			newViewerRolePermission(dashboardID, m.PERMISSION_ADMIN),
+		}
+		sc.updatePermissions = p
+		_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
+
+		if err != ErrGuardianPermissionExists {
+			sc.reportFailure(tc, ErrGuardianPermissionExists, err)
+		}
+		sc.reportSuccess()
 	})
 
-	Convey(desc, func() {
-		fn(sc)
+	tc = "When updating dashboard permissions with duplicate permission for admin role should not be allowed"
+	Convey(tc, func() {
+		p := []*m.DashboardAcl{
+			newAdminRolePermission(dashboardID, m.PERMISSION_ADMIN),
+		}
+		sc.updatePermissions = p
+		_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, p)
+
+		if err != ErrGuardianPermissionExists {
+			sc.reportFailure(tc, ErrGuardianPermissionExists, err)
+		}
+		sc.reportSuccess()
 	})
 }
 
-func userWithPermissionScenario(permission m.PermissionType, sc *scenarioContext, fn scenarioFunc) {
-	p := []*m.DashboardAclInfoDTO{
-		{OrgId: 1, DashboardId: 1, UserId: 1, Permission: permission},
+func (sc *scenarioContext) verifyUpdateDashboardPermissionsShouldBeAllowed(pt permissionType) {
+	if !sc.expectedFlags.canAdmin() {
+		return
+	}
+
+	for _, p := range []m.PermissionType{m.PERMISSION_ADMIN, m.PERMISSION_EDIT, m.PERMISSION_VIEW} {
+		tc := fmt.Sprintf("When updating dashboard permissions with %s permissions should be allowed", p.String())
+
+		Convey(tc, func() {
+			permissionList := []*m.DashboardAcl{}
+			switch pt {
+			case USER:
+				permissionList = []*m.DashboardAcl{
+					newEditorRolePermission(dashboardID, p),
+					newViewerRolePermission(dashboardID, p),
+					newCustomUserPermission(dashboardID, otherUserID, p),
+					newDefaultTeamPermission(dashboardID, p),
+				}
+			case TEAM:
+				permissionList = []*m.DashboardAcl{
+					newEditorRolePermission(dashboardID, p),
+					newViewerRolePermission(dashboardID, p),
+					newDefaultUserPermission(dashboardID, p),
+					newCustomTeamPermission(dashboardID, otherTeamID, p),
+				}
+			case EDITOR, VIEWER:
+				permissionList = []*m.DashboardAcl{
+					newEditorRolePermission(dashboardID, p),
+					newViewerRolePermission(dashboardID, p),
+					newDefaultUserPermission(dashboardID, p),
+					newDefaultTeamPermission(dashboardID, p),
+				}
+			}
+
+			sc.updatePermissions = permissionList
+			ok, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, permissionList)
+
+			if err != nil {
+				sc.reportFailure(tc, nil, err)
+			}
+			if !ok {
+				sc.reportFailure(tc, false, true)
+			}
+			sc.reportSuccess()
+		})
+	}
+}
+
+func (sc *scenarioContext) verifyUpdateDashboardPermissionsShouldNotBeAllowed(pt permissionType) {
+	if sc.expectedFlags.canAdmin() {
+		return
+	}
+
+	for _, p := range []m.PermissionType{m.PERMISSION_ADMIN, m.PERMISSION_EDIT, m.PERMISSION_VIEW} {
+		tc := fmt.Sprintf("When updating dashboard permissions with %s permissions should NOT be allowed", p.String())
+
+		Convey(tc, func() {
+			permissionList := []*m.DashboardAcl{
+				newEditorRolePermission(dashboardID, p),
+				newViewerRolePermission(dashboardID, p),
+			}
+			switch pt {
+			case USER:
+				permissionList = append(permissionList, []*m.DashboardAcl{
+					newCustomUserPermission(dashboardID, otherUserID, p),
+					newDefaultTeamPermission(dashboardID, p),
+				}...)
+			case TEAM:
+				permissionList = append(permissionList, []*m.DashboardAcl{
+					newDefaultUserPermission(dashboardID, p),
+					newCustomTeamPermission(dashboardID, otherTeamID, p),
+				}...)
+			}
+
+			sc.updatePermissions = permissionList
+			ok, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, permissionList)
+
+			if err != nil {
+				sc.reportFailure(tc, nil, err)
+			}
+			if ok {
+				sc.reportFailure(tc, true, false)
+			}
+			sc.reportSuccess()
+		})
+	}
+}
+
+func (sc *scenarioContext) verifyUpdateChildDashboardPermissionsShouldBeAllowed(pt permissionType, parentFolderPermission m.PermissionType) {
+	if !sc.expectedFlags.canAdmin() {
+		return
+	}
+
+	for _, p := range []m.PermissionType{m.PERMISSION_ADMIN, m.PERMISSION_EDIT, m.PERMISSION_VIEW} {
+		tc := fmt.Sprintf("When updating child dashboard permissions with %s permissions should be allowed", p.String())
+
+		Convey(tc, func() {
+			permissionList := []*m.DashboardAcl{}
+			switch pt {
+			case USER:
+				permissionList = []*m.DashboardAcl{
+					newEditorRolePermission(childDashboardID, p),
+					newViewerRolePermission(childDashboardID, p),
+					newCustomUserPermission(childDashboardID, otherUserID, p),
+					newDefaultTeamPermission(childDashboardID, p),
+				}
+			case TEAM:
+				permissionList = []*m.DashboardAcl{
+					newEditorRolePermission(childDashboardID, p),
+					newViewerRolePermission(childDashboardID, p),
+					newDefaultUserPermission(childDashboardID, p),
+					newCustomTeamPermission(childDashboardID, otherTeamID, p),
+				}
+			case EDITOR:
+				permissionList = []*m.DashboardAcl{
+					newViewerRolePermission(childDashboardID, p),
+					newDefaultUserPermission(childDashboardID, p),
+					newDefaultTeamPermission(childDashboardID, p),
+				}
+
+				// permission to update is higher than parent folder permission
+				if p > parentFolderPermission {
+					permissionList = append(permissionList, newEditorRolePermission(childDashboardID, p))
+				}
+			case VIEWER:
+				permissionList = []*m.DashboardAcl{
+					newEditorRolePermission(childDashboardID, p),
+					newDefaultUserPermission(childDashboardID, p),
+					newDefaultTeamPermission(childDashboardID, p),
+				}
+
+				// permission to update is higher than parent folder permission
+				if p > parentFolderPermission {
+					permissionList = append(permissionList, newViewerRolePermission(childDashboardID, p))
+				}
+			}
+
+			sc.updatePermissions = permissionList
+			ok, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, permissionList)
+
+			if err != nil {
+				sc.reportFailure(tc, nil, err)
+			}
+			if !ok {
+				sc.reportFailure(tc, false, true)
+			}
+			sc.reportSuccess()
+		})
+	}
+}
+
+func (sc *scenarioContext) verifyUpdateChildDashboardPermissionsShouldNotBeAllowed(pt permissionType, parentFolderPermission m.PermissionType) {
+	if sc.expectedFlags.canAdmin() {
+		return
+	}
+
+	for _, p := range []m.PermissionType{m.PERMISSION_ADMIN, m.PERMISSION_EDIT, m.PERMISSION_VIEW} {
+		tc := fmt.Sprintf("When updating child dashboard permissions with %s permissions should NOT be allowed", p.String())
+
+		Convey(tc, func() {
+			permissionList := []*m.DashboardAcl{}
+			switch pt {
+			case USER:
+				permissionList = []*m.DashboardAcl{
+					newEditorRolePermission(childDashboardID, p),
+					newViewerRolePermission(childDashboardID, p),
+					newCustomUserPermission(childDashboardID, otherUserID, p),
+					newDefaultTeamPermission(childDashboardID, p),
+				}
+			case TEAM:
+				permissionList = []*m.DashboardAcl{
+					newEditorRolePermission(childDashboardID, p),
+					newViewerRolePermission(childDashboardID, p),
+					newDefaultUserPermission(childDashboardID, p),
+					newCustomTeamPermission(childDashboardID, otherTeamID, p),
+				}
+			case EDITOR:
+				permissionList = []*m.DashboardAcl{
+					newViewerRolePermission(childDashboardID, p),
+					newDefaultUserPermission(childDashboardID, p),
+					newDefaultTeamPermission(childDashboardID, p),
+				}
+
+				// perminssion to update is higher than parent folder permission
+				if p > parentFolderPermission {
+					permissionList = append(permissionList, newEditorRolePermission(childDashboardID, p))
+				}
+			case VIEWER:
+				permissionList = []*m.DashboardAcl{
+					newEditorRolePermission(childDashboardID, p),
+					newDefaultUserPermission(childDashboardID, p),
+					newDefaultTeamPermission(childDashboardID, p),
+				}
+
+				// perminssion to update is higher than parent folder permission
+				if p > parentFolderPermission {
+					permissionList = append(permissionList, newViewerRolePermission(childDashboardID, p))
+				}
+			}
+
+			sc.updatePermissions = permissionList
+			ok, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, permissionList)
+
+			if err != nil {
+				sc.reportFailure(tc, nil, err)
+			}
+			if ok {
+				sc.reportFailure(tc, true, false)
+			}
+			sc.reportSuccess()
+		})
 	}
-	permissionScenario(fmt.Sprintf("and user has permission to %s item", permission), sc, p, fn)
 }
 
-func teamWithPermissionScenario(permission m.PermissionType, sc *scenarioContext, fn scenarioFunc) {
-	p := []*m.DashboardAclInfoDTO{
-		{OrgId: 1, DashboardId: 1, TeamId: 1, Permission: permission},
+func (sc *scenarioContext) verifyUpdateChildDashboardPermissionsWithOverrideShouldBeAllowed(pt permissionType, parentFolderPermission m.PermissionType) {
+	if !sc.expectedFlags.canAdmin() {
+		return
+	}
+
+	for _, p := range []m.PermissionType{m.PERMISSION_ADMIN, m.PERMISSION_EDIT, m.PERMISSION_VIEW} {
+		// perminssion to update is higher tban parent folder permission
+		if p > parentFolderPermission {
+			continue
+		}
+
+		tc := fmt.Sprintf("When updating child dashboard permissions overriding parent %s permission with %s permission should NOT be allowed", pt.String(), p.String())
+
+		Convey(tc, func() {
+			permissionList := []*m.DashboardAcl{}
+			switch pt {
+			case USER:
+				permissionList = []*m.DashboardAcl{
+					newDefaultUserPermission(childDashboardID, p),
+				}
+			case TEAM:
+				permissionList = []*m.DashboardAcl{
+					newDefaultTeamPermission(childDashboardID, p),
+				}
+			case EDITOR:
+				permissionList = []*m.DashboardAcl{
+					newEditorRolePermission(childDashboardID, p),
+				}
+			case VIEWER:
+				permissionList = []*m.DashboardAcl{
+					newViewerRolePermission(childDashboardID, p),
+				}
+			}
+
+			sc.updatePermissions = permissionList
+			_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, permissionList)
+
+			if err != ErrGuardianOverride {
+				sc.reportFailure(tc, ErrGuardianOverride, err)
+			}
+			sc.reportSuccess()
+		})
 	}
-	permissionScenario(fmt.Sprintf("and team has permission to %s item", permission), sc, p, fn)
 }
 
-func everyoneWithRoleScenario(role m.RoleType, permission m.PermissionType, sc *scenarioContext, fn scenarioFunc) {
-	p := []*m.DashboardAclInfoDTO{
-		{OrgId: 1, DashboardId: 1, UserId: -1, Role: &role, Permission: permission},
+func (sc *scenarioContext) verifyUpdateChildDashboardPermissionsWithOverrideShouldNotBeAllowed(pt permissionType, parentFolderPermission m.PermissionType) {
+	if !sc.expectedFlags.canAdmin() {
+		return
+	}
+
+	for _, p := range []m.PermissionType{m.PERMISSION_ADMIN, m.PERMISSION_EDIT, m.PERMISSION_VIEW} {
+		// perminssion to update is lower than/equal parent folder permission
+		if p <= parentFolderPermission {
+			continue
+		}
+
+		tc := fmt.Sprintf("When updating child dashboard permissions overriding parent %s permission with %s permission should be allowed", pt.String(), p.String())
+
+		Convey(tc, func() {
+			permissionList := []*m.DashboardAcl{}
+			switch pt {
+			case USER:
+				permissionList = []*m.DashboardAcl{
+					newDefaultUserPermission(childDashboardID, p),
+				}
+			case TEAM:
+				permissionList = []*m.DashboardAcl{
+					newDefaultTeamPermission(childDashboardID, p),
+				}
+			case EDITOR:
+				permissionList = []*m.DashboardAcl{
+					newEditorRolePermission(childDashboardID, p),
+				}
+			case VIEWER:
+				permissionList = []*m.DashboardAcl{
+					newViewerRolePermission(childDashboardID, p),
+				}
+			}
+
+			_, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, permissionList)
+			sc.updatePermissions = permissionList
+			ok, err := sc.g.CheckPermissionBeforeUpdate(m.PERMISSION_ADMIN, permissionList)
+
+			if err != nil {
+				sc.reportFailure(tc, nil, err)
+			}
+			if !ok {
+				sc.reportFailure(tc, false, true)
+			}
+			sc.reportSuccess()
+		})
 	}
-	permissionScenario(fmt.Sprintf("and everyone with %s role can %s item", role, permission), sc, p, fn)
 }

+ 256 - 0
pkg/services/guardian/guardian_util_test.go

@@ -0,0 +1,256 @@
+package guardian
+
+import (
+	"bytes"
+	"fmt"
+	"strings"
+	"testing"
+
+	"github.com/grafana/grafana/pkg/bus"
+	m "github.com/grafana/grafana/pkg/models"
+	. "github.com/smartystreets/goconvey/convey"
+)
+
+type scenarioContext struct {
+	t                  *testing.T
+	orgRoleScenario    string
+	permissionScenario string
+	g                  DashboardGuardian
+	givenUser          *m.SignedInUser
+	givenDashboardID   int64
+	givenPermissions   []*m.DashboardAclInfoDTO
+	givenTeams         []*m.Team
+	updatePermissions  []*m.DashboardAcl
+	expectedFlags      permissionFlags
+	callerFile         string
+	callerLine         int
+}
+
+type scenarioFunc func(c *scenarioContext)
+
+func orgRoleScenario(desc string, t *testing.T, role m.RoleType, fn scenarioFunc) {
+	user := &m.SignedInUser{
+		UserId:  userID,
+		OrgId:   orgID,
+		OrgRole: role,
+	}
+	guard := New(dashboardID, orgID, user)
+	sc := &scenarioContext{
+		t:                t,
+		orgRoleScenario:  desc,
+		givenUser:        user,
+		givenDashboardID: dashboardID,
+		g:                guard,
+	}
+
+	Convey(desc, func() {
+		fn(sc)
+	})
+}
+
+func permissionScenario(desc string, dashboardID int64, sc *scenarioContext, permissions []*m.DashboardAclInfoDTO, fn scenarioFunc) {
+	bus.ClearBusHandlers()
+
+	bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
+		if query.OrgId != sc.givenUser.OrgId {
+			sc.reportFailure("Invalid organization id for GetDashboardAclInfoListQuery", sc.givenUser.OrgId, query.OrgId)
+		}
+		if query.DashboardId != sc.givenDashboardID {
+			sc.reportFailure("Invalid dashboard id for GetDashboardAclInfoListQuery", sc.givenDashboardID, query.DashboardId)
+		}
+
+		query.Result = permissions
+		return nil
+	})
+
+	teams := []*m.Team{}
+
+	for _, p := range permissions {
+		if p.TeamId > 0 {
+			teams = append(teams, &m.Team{Id: p.TeamId})
+		}
+	}
+
+	bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
+		if query.OrgId != sc.givenUser.OrgId {
+			sc.reportFailure("Invalid organization id for GetTeamsByUserQuery", sc.givenUser.OrgId, query.OrgId)
+		}
+		if query.UserId != sc.givenUser.UserId {
+			sc.reportFailure("Invalid user id for GetTeamsByUserQuery", sc.givenUser.UserId, query.UserId)
+		}
+
+		query.Result = teams
+		return nil
+	})
+
+	sc.permissionScenario = desc
+	sc.g = New(dashboardID, sc.givenUser.OrgId, sc.givenUser)
+	sc.givenDashboardID = dashboardID
+	sc.givenPermissions = permissions
+	sc.givenTeams = teams
+
+	Convey(desc, func() {
+		fn(sc)
+	})
+}
+
+type permissionType uint8
+
+const (
+	USER permissionType = 1 << iota
+	TEAM
+	EDITOR
+	VIEWER
+)
+
+func (p permissionType) String() string {
+	names := map[uint8]string{
+		uint8(USER):   "user",
+		uint8(TEAM):   "team",
+		uint8(EDITOR): "editor role",
+		uint8(VIEWER): "viewer role",
+	}
+	return names[uint8(p)]
+}
+
+type permissionFlags uint8
+
+const (
+	NO_ACCESS permissionFlags = 1 << iota
+	CAN_ADMIN
+	CAN_EDIT
+	CAN_SAVE
+	CAN_VIEW
+	FULL_ACCESS   = CAN_ADMIN | CAN_EDIT | CAN_SAVE | CAN_VIEW
+	EDITOR_ACCESS = CAN_EDIT | CAN_SAVE | CAN_VIEW
+	VIEWER_ACCESS = CAN_VIEW
+)
+
+func (flag permissionFlags) canAdmin() bool {
+	return flag&CAN_ADMIN != 0
+}
+
+func (flag permissionFlags) canEdit() bool {
+	return flag&CAN_EDIT != 0
+}
+
+func (flag permissionFlags) canSave() bool {
+	return flag&CAN_SAVE != 0
+}
+
+func (flag permissionFlags) canView() bool {
+	return flag&CAN_VIEW != 0
+}
+
+func (flag permissionFlags) noAccess() bool {
+	return flag&(CAN_ADMIN|CAN_EDIT|CAN_SAVE|CAN_VIEW) == 0
+}
+
+func (f permissionFlags) String() string {
+	r := []string{}
+
+	if f.canAdmin() {
+		r = append(r, "admin")
+	}
+
+	if f.canEdit() {
+		r = append(r, "edit")
+	}
+
+	if f.canSave() {
+		r = append(r, "save")
+	}
+
+	if f.canView() {
+		r = append(r, "view")
+	}
+
+	if f.noAccess() {
+		r = append(r, "<no access>")
+	}
+
+	return strings.Join(r[:], ", ")
+}
+
+func (sc *scenarioContext) reportSuccess() {
+	So(true, ShouldBeTrue)
+}
+
+func (sc *scenarioContext) reportFailure(desc string, expected interface{}, actual interface{}) {
+	var buf bytes.Buffer
+	buf.WriteString("\n")
+	buf.WriteString(sc.orgRoleScenario)
+	buf.WriteString(" ")
+	buf.WriteString(sc.permissionScenario)
+	buf.WriteString("\n  ")
+	buf.WriteString(desc)
+	buf.WriteString("\n")
+	buf.WriteString(fmt.Sprintf("Source test: %s:%d\n", sc.callerFile, sc.callerLine))
+	buf.WriteString(fmt.Sprintf("Expected: %v\n", expected))
+	buf.WriteString(fmt.Sprintf("Actual: %v\n", actual))
+	buf.WriteString("Context:")
+	buf.WriteString(fmt.Sprintf("\n  Given user: orgRole=%s, id=%d, orgId=%d", sc.givenUser.OrgRole, sc.givenUser.UserId, sc.givenUser.OrgId))
+	buf.WriteString(fmt.Sprintf("\n  Given dashboard id: %d", sc.givenDashboardID))
+
+	for i, p := range sc.givenPermissions {
+		r := "<nil>"
+		if p.Role != nil {
+			r = string(*p.Role)
+		}
+		buf.WriteString(fmt.Sprintf("\n  Given permission (%d): dashboardId=%d, userId=%d, teamId=%d, role=%v, permission=%s", i, p.DashboardId, p.UserId, p.TeamId, r, p.Permission.String()))
+	}
+
+	for i, t := range sc.givenTeams {
+		buf.WriteString(fmt.Sprintf("\n  Given team (%d): id=%d", i, t.Id))
+	}
+
+	for i, p := range sc.updatePermissions {
+		r := "<nil>"
+		if p.Role != nil {
+			r = string(*p.Role)
+		}
+		buf.WriteString(fmt.Sprintf("\n  Update permission (%d): dashboardId=%d, userId=%d, teamId=%d, role=%v, permission=%s", i, p.DashboardId, p.UserId, p.TeamId, r, p.Permission.String()))
+	}
+
+	sc.t.Fatalf(buf.String())
+}
+
+func newCustomUserPermission(dashboardID int64, userID int64, permission m.PermissionType) *m.DashboardAcl {
+	return &m.DashboardAcl{OrgId: orgID, DashboardId: dashboardID, UserId: userID, Permission: permission}
+}
+
+func newDefaultUserPermission(dashboardID int64, permission m.PermissionType) *m.DashboardAcl {
+	return newCustomUserPermission(dashboardID, userID, permission)
+}
+
+func newCustomTeamPermission(dashboardID int64, teamID int64, permission m.PermissionType) *m.DashboardAcl {
+	return &m.DashboardAcl{OrgId: orgID, DashboardId: dashboardID, TeamId: teamID, Permission: permission}
+}
+
+func newDefaultTeamPermission(dashboardID int64, permission m.PermissionType) *m.DashboardAcl {
+	return newCustomTeamPermission(dashboardID, teamID, permission)
+}
+
+func newAdminRolePermission(dashboardID int64, permission m.PermissionType) *m.DashboardAcl {
+	return &m.DashboardAcl{OrgId: orgID, DashboardId: dashboardID, Role: &adminRole, Permission: permission}
+}
+
+func newEditorRolePermission(dashboardID int64, permission m.PermissionType) *m.DashboardAcl {
+	return &m.DashboardAcl{OrgId: orgID, DashboardId: dashboardID, Role: &editorRole, Permission: permission}
+}
+
+func newViewerRolePermission(dashboardID int64, permission m.PermissionType) *m.DashboardAcl {
+	return &m.DashboardAcl{OrgId: orgID, DashboardId: dashboardID, Role: &viewerRole, Permission: permission}
+}
+
+func toDto(acl *m.DashboardAcl) *m.DashboardAclInfoDTO {
+	return &m.DashboardAclInfoDTO{
+		OrgId:          acl.OrgId,
+		DashboardId:    acl.DashboardId,
+		UserId:         acl.UserId,
+		TeamId:         acl.TeamId,
+		Role:           acl.Role,
+		Permission:     acl.Permission,
+		PermissionName: acl.Permission.String(),
+	}
+}