Commit fa18b005 by Daniel Lee

WIP: delete permission in API

parent fff7b706
......@@ -250,6 +250,8 @@ func (hs *HttpServer) registerRoutes() {
r.Group("/:id/acl", func() {
r.Get("/", wrap(GetDashboardAcl))
r.Delete("/user/:userId", wrap(DeleteDashboardAclByUser))
r.Delete("/user-group/:userGroupId", wrap(DeleteDashboardAclByUserGroup))
}, reqSignedIn)
})
......
......@@ -29,3 +29,45 @@ func GetDashboardAcl(c *middleware.Context) Response {
return Json(200, &query.Result)
}
func DeleteDashboardAclByUser(c *middleware.Context) Response {
dashboardId := c.ParamsInt64(":id")
userId := c.ParamsInt64(":userId")
cmd := m.RemoveDashboardPermissionCommand{DashboardId: dashboardId, UserId: userId, OrgId: c.OrgId}
hasPermission, err := guardian.CanDeleteFromAcl(dashboardId, c.OrgRole, c.IsGrafanaAdmin, c.OrgId, c.UserId)
if err != nil {
return ApiError(500, "Failed to delete from Dashboard ACL", err)
}
if !hasPermission {
return Json(403, util.DynMap{"status": "Forbidden", "message": "Does not have access to this Dashboard ACL"})
}
if err := bus.Dispatch(&cmd); err != nil {
return ApiError(500, "Failed to delete permission for user", err)
}
return Json(200, "")
}
func DeleteDashboardAclByUserGroup(c *middleware.Context) Response {
dashboardId := c.ParamsInt64(":id")
userGroupId := c.ParamsInt64(":userGroupId")
cmd := m.RemoveDashboardPermissionCommand{DashboardId: dashboardId, UserGroupId: userGroupId, OrgId: c.OrgId}
hasPermission, err := guardian.CanDeleteFromAcl(dashboardId, c.OrgRole, c.IsGrafanaAdmin, c.OrgId, c.UserId)
if err != nil {
return ApiError(500, "Failed to delete from Dashboard ACL", err)
}
if !hasPermission {
return Json(403, util.DynMap{"status": "Forbidden", "message": "Does not have access to this Dashboard ACL"})
}
if err := bus.Dispatch(&cmd); err != nil {
return ApiError(500, "Failed to delete permission for user", err)
}
return Json(200, "")
}
......@@ -15,6 +15,8 @@ func TestDashboardAclApiEndpoint(t *testing.T) {
mockResult := []*models.DashboardAclInfoDTO{
{Id: 1, OrgId: 1, DashboardId: 1, UserId: 2, Permissions: models.PERMISSION_EDIT},
{Id: 2, OrgId: 1, DashboardId: 1, UserId: 3, Permissions: models.PERMISSION_VIEW},
{Id: 3, OrgId: 1, DashboardId: 1, UserGroupId: 1, Permissions: models.PERMISSION_EDIT},
{Id: 4, OrgId: 1, DashboardId: 1, UserGroupId: 2, Permissions: models.PERMISSION_READ_ONLY_EDIT},
}
bus.AddHandler("test", func(query *models.GetDashboardPermissionsQuery) error {
query.Result = mockResult
......@@ -22,7 +24,7 @@ func TestDashboardAclApiEndpoint(t *testing.T) {
})
Convey("When user is org admin", func() {
loggedInUserScenarioWithRole("When calling GET on", "/api/dashboard/1/acl", models.ROLE_ADMIN, func(sc *scenarioContext) {
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/1/acl", "/api/dashboards/:id/acl", models.ROLE_ADMIN, func(sc *scenarioContext) {
Convey("Should be able to access ACL", func() {
sc.handlerFunc = GetDashboardAcl
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
......@@ -37,14 +39,68 @@ func TestDashboardAclApiEndpoint(t *testing.T) {
})
})
Convey("When user is editor and not in the ACL", func() {
loggedInUserScenarioWithRole("When calling GET on", "/api/dashboard/1/acl", models.ROLE_EDITOR, func(sc *scenarioContext) {
Convey("When user is editor and in the ACL", func() {
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()
So(sc.resp.Code, ShouldEqual, 200)
})
})
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/1/acl/user/1", "/api/dashboards/:id/acl/user/:userId", 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("test3", func(cmd *models.RemoveDashboardPermissionCommand) error {
return nil
})
Convey("Should be able to delete permission", func() {
sc.handlerFunc = DeleteDashboardAclByUser
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 200)
})
})
Convey("When user is a member of a user group in the ACL with edit permission", func() {
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/1/acl/user/1", "/api/dashboards/:id/acl/user/:userId", models.ROLE_EDITOR, func(sc *scenarioContext) {
bus.AddHandler("test3", func(query *models.GetUserGroupsByUserQuery) error {
query.Result = []*models.UserGroup{{Id: 1, OrgId: 1, Name: "UG1"}}
return nil
})
bus.AddHandler("test3", func(cmd *models.RemoveDashboardPermissionCommand) error {
return nil
})
Convey("Should be able to delete permission", func() {
sc.handlerFunc = DeleteDashboardAclByUser
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 200)
})
})
})
})
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()
......@@ -52,6 +108,20 @@ func TestDashboardAclApiEndpoint(t *testing.T) {
So(sc.resp.Code, ShouldEqual, 403)
})
})
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/dashboards/1/acl/user/1", "/api/dashboards/:id/acl/user/:userId", models.ROLE_EDITOR, func(sc *scenarioContext) {
mockResult = append(mockResult, &models.DashboardAclInfoDTO{Id: 1, OrgId: 1, DashboardId: 1, UserId: 1, Permissions: models.PERMISSION_VIEW})
bus.AddHandler("test3", func(cmd *models.RemoveDashboardPermissionCommand) error {
return nil
})
Convey("Should be not be able to delete permission", func() {
sc.handlerFunc = DeleteDashboardAclByUser
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 403)
})
})
})
})
}
......@@ -56,10 +56,10 @@ func TestDataSourcesProxy(t *testing.T) {
}
func loggedInUserScenario(desc string, url string, fn scenarioFunc) {
loggedInUserScenarioWithRole(desc, url, models.ROLE_EDITOR, fn)
loggedInUserScenarioWithRole(desc, "GET", url, url, models.ROLE_EDITOR, fn)
}
func loggedInUserScenarioWithRole(desc string, url string, role models.RoleType, fn scenarioFunc) {
func loggedInUserScenarioWithRole(desc string, method string, url string, routePattern string, role models.RoleType, fn scenarioFunc) {
Convey(desc+" "+url, func() {
defer bus.ClearBusHandlers()
......@@ -89,7 +89,12 @@ func loggedInUserScenarioWithRole(desc string, url string, role models.RoleType,
return nil
})
sc.m.Get(url, sc.defaultHandler)
switch method {
case "GET":
sc.m.Get(routePattern, sc.defaultHandler)
case "DELETE":
sc.m.Delete(routePattern, sc.defaultHandler)
}
fn(sc)
})
......
......@@ -32,13 +32,45 @@ func CanViewAcl(dashboardId int64, role m.RoleType, isGrafanaAdmin bool, orgId i
return false, err
}
if len(filteredList) > 1 && filteredList[0] == dashboardId {
if len(filteredList) > 0 && filteredList[0] == dashboardId {
return true, nil
}
return false, nil
}
// CanDeleteFromAcl determines if a user has permission to delete from a dashboard's ACL
func CanDeleteFromAcl(dashboardId int64, role m.RoleType, isGrafanaAdmin bool, orgId int64, userId int64) (bool, error) {
if role == m.ROLE_ADMIN || isGrafanaAdmin {
return true, nil
}
permissions, err := getDashboardPermissions(dashboardId)
if err != nil {
return false, err
}
if len(permissions) == 0 {
return true, nil
}
userGroups, err := getUserGroupsByUser(userId)
for _, p := range permissions {
if p.UserId == userId && p.Permissions == m.PERMISSION_EDIT {
return true, nil
}
for _, ug := range userGroups {
if ug.Id == p.UserGroupId && p.Permissions == m.PERMISSION_EDIT {
return true, nil
}
}
}
return false, nil
}
func getUser(userId int64) (*m.SignedInUser, error) {
query := m.GetSignedInUserQuery{UserId: userId}
err := bus.Dispatch(&query)
......@@ -52,3 +84,17 @@ func getAllowedDashboards(dashList []int64, orgId int64, userId int64) ([]int64,
return query.Result, err
}
func getDashboardPermissions(dashboardId int64) ([]*m.DashboardAclInfoDTO, error) {
query := m.GetDashboardPermissionsQuery{DashboardId: dashboardId}
err := bus.Dispatch(&query)
return query.Result, err
}
func getUserGroupsByUser(userId int64) ([]*m.UserGroup, error) {
query := m.GetUserGroupsByUserQuery{UserId: userId}
err := bus.Dispatch(&query)
return query.Result, err
}
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