Alerting: Return 403 if no datasource access or quota has been exceeded (#101522)

This commit is contained in:
Alexander Akhmetov
2025-03-04 10:04:47 +01:00
committed by GitHub
parent 859c12bbb4
commit 60827fe499
2 changed files with 59 additions and 7 deletions
@@ -12,6 +12,7 @@ import (
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/infra/log"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/datasources"
@@ -22,6 +23,7 @@ import (
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
"github.com/grafana/grafana/pkg/services/ngalert/store"
"github.com/grafana/grafana/pkg/services/ngalert/tests/fakes"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
@@ -151,6 +153,32 @@ func TestRouteConvertPrometheusPostRuleGroup(t *testing.T) {
require.NotNil(t, remaining)
})
t.Run("with no access to the datasource should return 403", func(t *testing.T) {
acFake := &acfakes.FakeRuleService{}
srv, _, _, _ := createConvertPrometheusSrv(t, withFakeAccessControlRuleService(acFake))
acFake.AuthorizeRuleChangesFunc = func(context.Context, identity.Requester, *store.GroupDelta) error {
return datasources.ErrDataSourceAccessDenied
}
rc := createRequestCtx()
response := srv.RouteConvertPrometheusPostRuleGroup(rc, "folder", simpleGroup)
require.Equal(t, http.StatusForbidden, response.Status())
require.Contains(t, string(response.Body()), "data source access denied")
})
t.Run("when alert rule quota limit exceeded", func(t *testing.T) {
quotas := &provisioning.MockQuotaChecker{}
quotas.EXPECT().LimitExceeded()
srv, _, _, _ := createConvertPrometheusSrv(t, withQuotaChecker(quotas))
rc := createRequestCtx()
response := srv.RouteConvertPrometheusPostRuleGroup(rc, "folder", simpleGroup)
require.Equal(t, http.StatusForbidden, response.Status())
require.Contains(t, string(response.Body()), "quota has been exceeded")
})
t.Run("with valid pause header values should return 202", func(t *testing.T) {
testCases := []struct {
name string
@@ -749,7 +777,9 @@ func TestRouteConvertPrometheusDeleteRuleGroup(t *testing.T) {
}
type convertPrometheusSrvOptions struct {
provenanceStore provisioning.ProvisioningStore
provenanceStore provisioning.ProvisioningStore
fakeAccessControlRuleService *acfakes.FakeRuleService
quotaChecker *provisioning.MockQuotaChecker
}
type convertPrometheusSrvOptionsFunc func(*convertPrometheusSrvOptions)
@@ -760,11 +790,29 @@ func withProvenanceStore(store provisioning.ProvisioningStore) convertPrometheus
}
}
func withFakeAccessControlRuleService(service *acfakes.FakeRuleService) convertPrometheusSrvOptionsFunc {
return func(opts *convertPrometheusSrvOptions) {
opts.fakeAccessControlRuleService = service
}
}
func withQuotaChecker(checker *provisioning.MockQuotaChecker) convertPrometheusSrvOptionsFunc {
return func(opts *convertPrometheusSrvOptions) {
opts.quotaChecker = checker
}
}
func createConvertPrometheusSrv(t *testing.T, opts ...convertPrometheusSrvOptionsFunc) (*ConvertPrometheusSrv, datasources.CacheService, *fakes.RuleStore, *foldertest.FakeService) {
t.Helper()
// By default the quota checker will allow the operation
quotas := &provisioning.MockQuotaChecker{}
quotas.EXPECT().LimitOK()
options := convertPrometheusSrvOptions{
provenanceStore: fakes.NewFakeProvisioningStore(),
provenanceStore: fakes.NewFakeProvisioningStore(),
fakeAccessControlRuleService: &acfakes.FakeRuleService{},
quotaChecker: quotas,
}
for _, opt := range opts {
@@ -782,23 +830,20 @@ func createConvertPrometheusSrv(t *testing.T, opts ...convertPrometheusSrvOption
}
dsCache.DataSources = append(dsCache.DataSources, ds)
quotas := &provisioning.MockQuotaChecker{}
quotas.EXPECT().LimitOK()
folderService := foldertest.NewFakeService()
alertRuleService := provisioning.NewAlertRuleService(
ruleStore,
options.provenanceStore,
folderService,
quotas,
options.quotaChecker,
&provisioning.NopTransactionManager{},
60,
10,
100,
log.New("test"),
&provisioning.NotificationSettingsValidatorProviderFake{},
&acfakes.FakeRuleService{},
options.fakeAccessControlRuleService,
)
cfg := &setting.UnifiedAlertingSettings{
+7
View File
@@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/errutil"
"github.com/grafana/grafana/pkg/services/datasources"
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/ngalert/models"
)
var (
@@ -42,5 +43,11 @@ func errorToResponse(err error) response.Response {
if errors.Is(err, errFolderAccess) {
return toNamespaceErrorResponse(err)
}
if errors.Is(err, datasources.ErrDataSourceAccessDenied) {
return ErrResp(http.StatusForbidden, err, "")
}
if errors.Is(err, models.ErrQuotaReached) {
return ErrResp(http.StatusForbidden, err, "")
}
return ErrResp(http.StatusInternalServerError, err, "")
}