Jelajahi Sumber

alerting: add permission check in api for pausing alerts

Daniel Lee 8 tahun lalu
induk
melakukan
f5107d5023
4 mengubah file dengan 114 tambahan dan 29 penghapusan
  1. 17 18
      pkg/api/alerting.go
  2. 97 0
      pkg/api/alerting_test.go
  3. 0 4
      pkg/models/alert.go
  4. 0 7
      pkg/services/sqlstore/alert.go

+ 17 - 18
pkg/api/alerting.go

@@ -8,6 +8,7 @@ import (
 	"github.com/grafana/grafana/pkg/middleware"
 	"github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/services/alerting"
+	"github.com/grafana/grafana/pkg/services/guardian"
 )
 
 func ValidateOrgAlert(c *middleware.Context) {
@@ -155,24 +156,6 @@ func GetAlert(c *middleware.Context) Response {
 	return Json(200, &query.Result)
 }
 
-// DEL /api/alerts/:id
-func DelAlert(c *middleware.Context) Response {
-	alertId := c.ParamsInt64(":alertId")
-
-	if alertId == 0 {
-		return ApiError(401, "Failed to parse alertid", nil)
-	}
-
-	cmd := models.DeleteAlertCommand{AlertId: alertId}
-
-	if err := bus.Dispatch(&cmd); err != nil {
-		return ApiError(500, "Failed to delete alert", err)
-	}
-
-	var resp = map[string]interface{}{"alertId": alertId}
-	return Json(200, resp)
-}
-
 func GetAlertNotifiers(c *middleware.Context) Response {
 	return Json(200, alerting.GetNotifiers())
 }
@@ -267,6 +250,22 @@ func NotificationTest(c *middleware.Context, dto dtos.NotificationTestCommand) R
 //POST /api/alerts/:alertId/pause
 func PauseAlert(c *middleware.Context, dto dtos.PauseAlertCommand) Response {
 	alertId := c.ParamsInt64("alertId")
+
+	query := models.GetAlertByIdQuery{Id: alertId}
+
+	if err := bus.Dispatch(&query); err != nil {
+		return ApiError(500, "Get Alert failed", err)
+	}
+
+	guardian := guardian.NewDashboardGuardian(query.Result.DashboardId, c.OrgId, c.SignedInUser)
+	if canEdit, err := guardian.CanEdit(); err != nil || !canEdit {
+		if err != nil {
+			return ApiError(500, "Error while checking permissions for Alert", err)
+		}
+
+		return ApiError(403, "Access denied to this dashboard and alert", nil)
+	}
+
 	cmd := models.PauseAlertCommand{
 		OrgId:    c.OrgId,
 		AlertIds: []int64{alertId},

+ 97 - 0
pkg/api/alerting_test.go

@@ -0,0 +1,97 @@
+package api
+
+import (
+	"testing"
+
+	"github.com/grafana/grafana/pkg/api/dtos"
+	"github.com/grafana/grafana/pkg/bus"
+	"github.com/grafana/grafana/pkg/middleware"
+	m "github.com/grafana/grafana/pkg/models"
+
+	. "github.com/smartystreets/goconvey/convey"
+)
+
+func TestAlertingApiEndpoint(t *testing.T) {
+	Convey("Given an alert in a dashboard with an acl", t, func() {
+
+		singleAlert := &m.Alert{Id: 1, DashboardId: 1, Name: "singlealert"}
+
+		bus.AddHandler("test", func(query *m.GetAlertByIdQuery) error {
+			query.Result = singleAlert
+			return nil
+		})
+
+		viewerRole := m.ROLE_VIEWER
+		editorRole := m.ROLE_EDITOR
+
+		aclMockResp := []*m.DashboardAclInfoDTO{}
+		bus.AddHandler("test", func(query *m.GetDashboardAclInfoListQuery) error {
+			query.Result = aclMockResp
+			return nil
+		})
+
+		bus.AddHandler("test", func(query *m.GetTeamsByUserQuery) error {
+			query.Result = []*m.Team{}
+			return nil
+		})
+
+		Convey("When user is editor and not in the ACL", func() {
+			Convey("Should not be able to pause the alert", func() {
+				cmd := dtos.PauseAlertCommand{
+					AlertId: 1,
+					Paused:  true,
+				}
+				postAlertScenario("When calling POST on", "/api/alerts/1/pause", "/api/alerts/:alertId/pause", m.ROLE_EDITOR, cmd, func(sc *scenarioContext) {
+					CallPauseAlert(sc)
+					So(sc.resp.Code, ShouldEqual, 403)
+				})
+			})
+		})
+
+		Convey("When user is editor and dashboard has default ACL", func() {
+			aclMockResp = []*m.DashboardAclInfoDTO{
+				{Role: &viewerRole, Permission: m.PERMISSION_VIEW},
+				{Role: &editorRole, Permission: m.PERMISSION_EDIT},
+			}
+
+			Convey("Should be able to pause the alert", func() {
+				cmd := dtos.PauseAlertCommand{
+					AlertId: 1,
+					Paused:  true,
+				}
+				postAlertScenario("When calling POST on", "/api/alerts/1/pause", "/api/alerts/:alertId/pause", m.ROLE_EDITOR, cmd, func(sc *scenarioContext) {
+					CallPauseAlert(sc)
+					So(sc.resp.Code, ShouldEqual, 200)
+				})
+			})
+		})
+	})
+}
+
+func CallPauseAlert(sc *scenarioContext) {
+	bus.AddHandler("test", func(cmd *m.PauseAlertCommand) error {
+		return nil
+	})
+
+	sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
+}
+
+func postAlertScenario(desc string, url string, routePattern string, role m.RoleType, cmd dtos.PauseAlertCommand, fn scenarioFunc) {
+	Convey(desc+" "+url, func() {
+		defer bus.ClearBusHandlers()
+
+		sc := setupScenarioContext(url)
+		sc.defaultHandler = wrap(func(c *middleware.Context) Response {
+			sc.context = c
+			sc.context.UserId = TestUserID
+			sc.context.OrgId = TestOrgID
+			sc.context.OrgRole = role
+
+			return PauseAlert(c, cmd)
+		})
+
+		sc.m.Post(routePattern, sc.defaultHandler)
+
+		fn(sc)
+	})
+}

+ 0 - 4
pkg/models/alert.go

@@ -159,10 +159,6 @@ type SetAlertStateCommand struct {
 	Timestamp time.Time
 }
 
-type DeleteAlertCommand struct {
-	AlertId int64
-}
-
 //Queries
 type GetAlertsQuery struct {
 	OrgId       int64

+ 0 - 7
pkg/services/sqlstore/alert.go

@@ -14,7 +14,6 @@ func init() {
 	bus.AddHandler("sql", SaveAlerts)
 	bus.AddHandler("sql", HandleAlertsQuery)
 	bus.AddHandler("sql", GetAlertById)
-	bus.AddHandler("sql", DeleteAlertById)
 	bus.AddHandler("sql", GetAllAlertQueryHandler)
 	bus.AddHandler("sql", SetAlertState)
 	bus.AddHandler("sql", GetAlertStatesForDashboard)
@@ -61,12 +60,6 @@ func deleteAlertByIdInternal(alertId int64, reason string, sess *DBSession) erro
 	return nil
 }
 
-func DeleteAlertById(cmd *m.DeleteAlertCommand) error {
-	return inTransaction(func(sess *DBSession) error {
-		return deleteAlertByIdInternal(cmd.AlertId, "DeleteAlertCommand", sess)
-	})
-}
-
 func HandleAlertsQuery(query *m.GetAlertsQuery) error {
 	var sql bytes.Buffer
 	params := make([]interface{}, 0)