Alerting: Add access control to receiver status API (#94287)

This commit is contained in:
Yuri Tseretyan
2024-10-07 15:21:29 -04:00
committed by GitHub
parent cc40211f71
commit 4c1cb79069
3 changed files with 28 additions and 2 deletions

View File

@@ -110,6 +110,7 @@ func (api *API) RegisterAPIEndpoints(m *metrics.API) {
api.RuleStore, api.RuleStore,
ruleAuthzService, ruleAuthzService,
), ),
receiverAuthz: accesscontrol.NewReceiverAccess[ReceiverStatus](api.AccessControl, false),
}, },
), m) ), m)
// Register endpoints for proxying to Prometheus-compatible backends. // Register endpoints for proxying to Prometheus-compatible backends.

View File

@@ -12,12 +12,14 @@ import (
alertingNotify "github.com/grafana/alerting/notify" alertingNotify "github.com/grafana/alerting/notify"
"github.com/grafana/grafana/pkg/api/response" "github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions" apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/ngalert/notifier" "github.com/grafana/grafana/pkg/services/ngalert/notifier"
"github.com/grafana/grafana/pkg/services/ngalert/notifier/legacy_storage"
"github.com/grafana/grafana/pkg/services/ngalert/store" "github.com/grafana/grafana/pkg/services/ngalert/store"
"github.com/grafana/grafana/pkg/services/org" "github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
@@ -28,6 +30,10 @@ const (
maxTestReceiversTimeout = 30 * time.Second maxTestReceiversTimeout = 30 * time.Second
) )
type receiversAuthz interface {
FilterRead(ctx context.Context, user identity.Requester, receivers ...ReceiverStatus) ([]ReceiverStatus, error)
}
type AlertmanagerSrv struct { type AlertmanagerSrv struct {
log log.Logger log log.Logger
ac accesscontrol.AccessControl ac accesscontrol.AccessControl
@@ -35,6 +41,7 @@ type AlertmanagerSrv struct {
crypto notifier.Crypto crypto notifier.Crypto
silenceSvc SilenceService silenceSvc SilenceService
featureManager featuremgmt.FeatureToggles featureManager featuremgmt.FeatureToggles
receiverAuthz receiversAuthz
} }
type UnknownReceiverError struct { type UnknownReceiverError struct {
@@ -237,7 +244,15 @@ func (srv AlertmanagerSrv) RouteGetReceivers(c *contextmodel.ReqContext) respons
if err != nil { if err != nil {
return ErrResp(http.StatusInternalServerError, err, "failed to retrieve receivers") return ErrResp(http.StatusInternalServerError, err, "failed to retrieve receivers")
} }
return response.JSON(http.StatusOK, rcvs) statuses := make([]ReceiverStatus, 0, len(rcvs))
for _, rcv := range rcvs { // TODO this is temporary so we can use authz filter logic.
statuses = append(statuses, ReceiverStatus(rcv))
}
statuses, err = srv.receiverAuthz.FilterRead(c.Req.Context(), c.SignedInUser, statuses...)
if err != nil {
response.ErrOrFallback(http.StatusInternalServerError, "failed to apply permissions to the receivers", err)
}
return response.JSON(http.StatusOK, statuses)
} }
func (srv AlertmanagerSrv) RoutePostTestReceivers(c *contextmodel.ReqContext, body apimodels.TestReceiversConfigBodyParams) response.Response { func (srv AlertmanagerSrv) RoutePostTestReceivers(c *contextmodel.ReqContext, body apimodels.TestReceiversConfigBodyParams) response.Response {
@@ -368,3 +383,9 @@ func (srv AlertmanagerSrv) AlertmanagerFor(orgID int64) (notifier.Alertmanager,
srv.log.Error("Unable to obtain the org's Alertmanager", "error", err) srv.log.Error("Unable to obtain the org's Alertmanager", "error", err)
return nil, response.Error(http.StatusInternalServerError, "unable to obtain org's Alertmanager", err) return nil, response.Error(http.StatusInternalServerError, "unable to obtain org's Alertmanager", err)
} }
type ReceiverStatus apimodels.Receiver
func (rs ReceiverStatus) GetUID() string {
return legacy_storage.NameToUid(rs.Name)
}

View File

@@ -210,7 +210,11 @@ func (api *API) authorize(method, path string) web.Handler {
case http.MethodPost + "/api/alertmanager/grafana/config/history/{id}/_activate": case http.MethodPost + "/api/alertmanager/grafana/config/history/{id}/_activate":
eval = ac.EvalAny(ac.EvalPermission(ac.ActionAlertingNotificationsWrite)) eval = ac.EvalAny(ac.EvalPermission(ac.ActionAlertingNotificationsWrite))
case http.MethodGet + "/api/alertmanager/grafana/config/api/v1/receivers": case http.MethodGet + "/api/alertmanager/grafana/config/api/v1/receivers":
eval = ac.EvalPermission(ac.ActionAlertingNotificationsRead) eval = ac.EvalAny(
ac.EvalPermission(ac.ActionAlertingNotificationsRead),
ac.EvalPermission(ac.ActionAlertingReceiversRead),
ac.EvalPermission(ac.ActionAlertingReceiversReadSecrets),
)
case http.MethodPost + "/api/alertmanager/grafana/config/api/v1/receivers/test": case http.MethodPost + "/api/alertmanager/grafana/config/api/v1/receivers/test":
eval = ac.EvalAny( eval = ac.EvalAny(
ac.EvalPermission(ac.ActionAlertingNotificationsWrite), ac.EvalPermission(ac.ActionAlertingNotificationsWrite),