diff --git a/pkg/api/dashboard_acl.go b/pkg/api/dashboard_acl.go index 8feb464ca5e..e1791563f28 100644 --- a/pkg/api/dashboard_acl.go +++ b/pkg/api/dashboard_acl.go @@ -16,7 +16,7 @@ func GetDashboardAclList(c *middleware.Context) Response { guardian := guardian.NewDashboardGuardian(dashId, c.OrgId, c.SignedInUser) - if canView, err := guardian.CanView(); err != nil || !canView { + if canAdmin, err := guardian.CanAdmin(); err != nil || !canAdmin { return dashboardGuardianResponse(err) } @@ -32,7 +32,7 @@ func UpdateDashboardAcl(c *middleware.Context, apiCmd dtos.UpdateDashboardAclCom dashId := c.ParamsInt64(":dashboardId") guardian := guardian.NewDashboardGuardian(dashId, c.OrgId, c.SignedInUser) - if canSave, err := guardian.CanSave(); err != nil || !canSave { + if canAdmin, err := guardian.CanAdmin(); err != nil || !canAdmin { return dashboardGuardianResponse(err) } @@ -68,7 +68,7 @@ func DeleteDashboardAcl(c *middleware.Context) Response { aclId := c.ParamsInt64(":aclId") guardian := guardian.NewDashboardGuardian(dashId, c.OrgId, c.SignedInUser) - if canSave, err := guardian.CanSave(); err != nil || !canSave { + if canAdmin, err := guardian.CanAdmin(); err != nil || !canAdmin { return dashboardGuardianResponse(err) } diff --git a/pkg/api/dashboard_acl_test.go b/pkg/api/dashboard_acl_test.go index 3780018026b..7d8633fad25 100644 --- a/pkg/api/dashboard_acl_test.go +++ b/pkg/api/dashboard_acl_test.go @@ -13,17 +13,13 @@ import ( func TestDashboardAclApiEndpoint(t *testing.T) { Convey("Given a dashboard acl", t, func() { mockResult := []*models.DashboardAcl{ - {Id: 1, OrgId: 1, DashboardId: 1, UserId: 2, Permission: models.PERMISSION_EDIT}, - {Id: 2, OrgId: 1, DashboardId: 1, UserId: 3, Permission: models.PERMISSION_VIEW}, - {Id: 3, OrgId: 1, DashboardId: 1, UserGroupId: 1, Permission: models.PERMISSION_EDIT}, - {Id: 4, OrgId: 1, DashboardId: 1, UserGroupId: 2, Permission: models.PERMISSION_READ_ONLY_EDIT}, - } - dtoRes := []*models.DashboardAclInfoDTO{ - {Id: 1, OrgId: 1, DashboardId: 1, UserId: 2, Permission: models.PERMISSION_EDIT}, - {Id: 2, OrgId: 1, DashboardId: 1, UserId: 3, Permission: models.PERMISSION_VIEW}, - {Id: 3, OrgId: 1, DashboardId: 1, UserGroupId: 1, Permission: models.PERMISSION_EDIT}, - {Id: 4, OrgId: 1, DashboardId: 1, UserGroupId: 2, Permission: models.PERMISSION_READ_ONLY_EDIT}, + {Id: 1, OrgId: 1, DashboardId: 1, UserId: 2, Permission: models.PERMISSION_VIEW}, + {Id: 2, OrgId: 1, DashboardId: 1, UserId: 3, Permission: models.PERMISSION_EDIT}, + {Id: 3, OrgId: 1, DashboardId: 1, UserId: 4, Permission: models.PERMISSION_ADMIN}, + {Id: 4, OrgId: 1, DashboardId: 1, UserGroupId: 1, Permission: models.PERMISSION_VIEW}, + {Id: 5, OrgId: 1, DashboardId: 1, UserGroupId: 2, Permission: models.PERMISSION_ADMIN}, } + dtoRes := transformDashboardAclsToDTOs(mockResult) bus.AddHandler("test", func(query *models.GetDashboardAclInfoListQuery) error { query.Result = dtoRes @@ -51,15 +47,16 @@ func TestDashboardAclApiEndpoint(t *testing.T) { respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes()) So(err, ShouldBeNil) + So(len(respJSON.MustArray()), ShouldEqual, 5) So(respJSON.GetIndex(0).Get("userId").MustInt(), ShouldEqual, 2) - So(respJSON.GetIndex(0).Get("permissions").MustInt(), ShouldEqual, models.PERMISSION_EDIT) + So(respJSON.GetIndex(0).Get("permission").MustInt(), ShouldEqual, models.PERMISSION_VIEW) }) }) }) - Convey("When user is editor and in the ACL", func() { + Convey("When user is editor and has admin permission in the ACL", func() { loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/1/acl", "/api/dashboards/id/:dashboardId/acl", models.ROLE_EDITOR, func(sc *scenarioContext) { - mockResult = append(mockResult, &models.DashboardAcl{Id: 1, OrgId: 1, DashboardId: 1, UserId: 1, Permission: models.PERMISSION_EDIT}) + mockResult = append(mockResult, &models.DashboardAcl{Id: 1, OrgId: 1, DashboardId: 1, UserId: 1, Permission: models.PERMISSION_ADMIN}) Convey("Should be able to access ACL", func() { sc.handlerFunc = GetDashboardAclList @@ -70,7 +67,7 @@ func TestDashboardAclApiEndpoint(t *testing.T) { }) loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/id/1/acl/1", "/api/dashboards/id/:dashboardId/acl/:aclId", models.ROLE_EDITOR, func(sc *scenarioContext) { - mockResult = append(mockResult, &models.DashboardAcl{Id: 1, OrgId: 1, DashboardId: 1, UserId: 1, Permission: models.PERMISSION_EDIT}) + mockResult = append(mockResult, &models.DashboardAcl{Id: 1, OrgId: 1, DashboardId: 1, UserId: 1, Permission: models.PERMISSION_ADMIN}) bus.AddHandler("test3", func(cmd *models.RemoveDashboardAclCommand) error { return nil @@ -84,9 +81,9 @@ func TestDashboardAclApiEndpoint(t *testing.T) { }) }) - Convey("When user is a member of a user group in the ACL with edit permission", func() { + Convey("When user is a member of a user group in the ACL with admin permission", func() { loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/id/1/acl/1", "/api/dashboards/id/:dashboardsId/acl/:aclId", models.ROLE_EDITOR, func(sc *scenarioContext) { - userGroupResp = append(userGroupResp, &models.UserGroup{Id: 1, OrgId: 1, Name: "UG1"}) + userGroupResp = append(userGroupResp, &models.UserGroup{Id: 2, OrgId: 1, Name: "UG2"}) bus.AddHandler("test3", func(cmd *models.RemoveDashboardAclCommand) error { return nil @@ -102,6 +99,34 @@ func TestDashboardAclApiEndpoint(t *testing.T) { }) }) + Convey("When user is editor and has edit permission in the ACL", func() { + loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/1/acl", "/api/dashboards/id/:dashboardId/acl", models.ROLE_EDITOR, func(sc *scenarioContext) { + mockResult = append(mockResult, &models.DashboardAcl{Id: 1, OrgId: 1, DashboardId: 1, UserId: 1, Permission: models.PERMISSION_EDIT}) + + Convey("Should not be able to access ACL", func() { + sc.handlerFunc = GetDashboardAclList + sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec() + + So(sc.resp.Code, ShouldEqual, 403) + }) + }) + + loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/id/1/acl/1", "/api/dashboards/id/:dashboardId/acl/:aclId", models.ROLE_EDITOR, func(sc *scenarioContext) { + mockResult = append(mockResult, &models.DashboardAcl{Id: 1, OrgId: 1, DashboardId: 1, UserId: 1, Permission: models.PERMISSION_EDIT}) + + bus.AddHandler("test3", func(cmd *models.RemoveDashboardAclCommand) error { + return nil + }) + + Convey("Should be not be able to delete permission", func() { + sc.handlerFunc = DeleteDashboardAcl + sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec() + + So(sc.resp.Code, ShouldEqual, 403) + }) + }) + }) + Convey("When user is editor and not in the ACL", func() { loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/1/acl", "/api/dashboards/id/:dashboardsId/acl", models.ROLE_EDITOR, func(sc *scenarioContext) { @@ -129,3 +154,21 @@ func TestDashboardAclApiEndpoint(t *testing.T) { }) }) } + +func transformDashboardAclsToDTOs(acls []*models.DashboardAcl) []*models.DashboardAclInfoDTO { + dtos := make([]*models.DashboardAclInfoDTO, 0) + + for _, acl := range acls { + dto := &models.DashboardAclInfoDTO{ + Id: acl.Id, + OrgId: acl.OrgId, + DashboardId: acl.DashboardId, + Permission: acl.Permission, + UserId: acl.UserId, + UserGroupId: acl.UserGroupId, + } + dtos = append(dtos, dto) + } + + return dtos +} diff --git a/pkg/services/guardian/guardian.go b/pkg/services/guardian/guardian.go index 85f528c5f76..54248455828 100644 --- a/pkg/services/guardian/guardian.go +++ b/pkg/services/guardian/guardian.go @@ -36,6 +36,10 @@ func (g *DashboardGuardian) CanView() (bool, error) { return g.HasPermission(m.PERMISSION_VIEW) } +func (g *DashboardGuardian) CanAdmin() (bool, error) { + return g.HasPermission(m.PERMISSION_ADMIN) +} + func (g *DashboardGuardian) HasPermission(permission m.PermissionType) (bool, error) { if g.user.OrgRole == m.ROLE_ADMIN { return true, nil