Alerting: Fix prometheus API to check folder permissions (#36301)
This commit is contained in:
@@ -2,6 +2,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
@@ -76,6 +77,13 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *models.ReqContext) response.Res
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
groupId, namespaceUID, namespace := r[0], r[1], r[2]
|
groupId, namespaceUID, namespace := r[0], r[1], r[2]
|
||||||
|
if _, err := srv.store.GetNamespaceByUID(namespaceUID, c.SignedInUser.OrgId, c.SignedInUser); err != nil {
|
||||||
|
if errors.Is(err, models.ErrFolderAccessDenied) {
|
||||||
|
// do not include it in the response
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return toNamespaceErrorResponse(err)
|
||||||
|
}
|
||||||
alertRuleQuery := ngmodels.ListRuleGroupAlertRulesQuery{OrgID: c.SignedInUser.OrgId, NamespaceUID: namespaceUID, RuleGroup: groupId}
|
alertRuleQuery := ngmodels.ListRuleGroupAlertRulesQuery{OrgID: c.SignedInUser.OrgId, NamespaceUID: namespaceUID, RuleGroup: groupId}
|
||||||
if err := srv.store.GetRuleGroupAlertRules(&alertRuleQuery); err != nil {
|
if err := srv.store.GetRuleGroupAlertRules(&alertRuleQuery); err != nil {
|
||||||
ruleResponse.DiscoveryBase.Status = "error"
|
ruleResponse.DiscoveryBase.Status = "error"
|
||||||
|
|||||||
@@ -542,7 +542,7 @@ func (st DBstore) UpdateRuleGroup(cmd UpdateRuleGroupCmd) error {
|
|||||||
func (st DBstore) GetOrgRuleGroups(query *ngmodels.ListOrgRuleGroupsQuery) error {
|
func (st DBstore) GetOrgRuleGroups(query *ngmodels.ListOrgRuleGroupsQuery) error {
|
||||||
return st.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
return st.SQLStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
|
||||||
var ruleGroups [][]string
|
var ruleGroups [][]string
|
||||||
q := "SELECT DISTINCT rule_group, namespace_uid, (select title from dashboard where org_id = alert_rule.org_id and uid = alert_rule.namespace_uid) FROM alert_rule WHERE org_id = ?"
|
q := "SELECT DISTINCT rule_group, namespace_uid, (select title from dashboard where org_id = alert_rule.org_id and uid = alert_rule.namespace_uid) AS namespace_title FROM alert_rule WHERE org_id = ? ORDER BY namespace_title"
|
||||||
if err := sess.SQL(q, query.OrgID).Find(&ruleGroups); err != nil {
|
if err := sess.SQL(q, query.OrgID).Find(&ruleGroups); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -258,3 +258,150 @@ func TestPrometheusRules(t *testing.T) {
|
|||||||
}, 18*time.Second, 2*time.Second)
|
}, 18*time.Second, 2*time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPrometheusRulesPermissions(t *testing.T) {
|
||||||
|
dir, path := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{
|
||||||
|
EnableFeatureToggles: []string{"ngalert"},
|
||||||
|
DisableAnonymous: true,
|
||||||
|
})
|
||||||
|
store := testinfra.SetUpDatabase(t, dir)
|
||||||
|
// override bus to get the GetSignedInUserQuery handler
|
||||||
|
store.Bus = bus.GetBus()
|
||||||
|
grafanaListedAddr := testinfra.StartGrafana(t, dir, path, store)
|
||||||
|
|
||||||
|
// Create a user to make authenticated requests
|
||||||
|
require.NoError(t, createUser(t, store, models.ROLE_EDITOR, "grafana", "password"))
|
||||||
|
|
||||||
|
// Create a namespace under default organisation (orgID = 1) where we'll save some alerts.
|
||||||
|
_, err := createFolder(t, store, 0, "folder1")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Create another namespace under default organisation (orgID = 1) where we'll save some alerts.
|
||||||
|
_, err = createFolder(t, store, 0, "folder2")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Create rule under folder1
|
||||||
|
createRule(t, grafanaListedAddr, "folder1", "grafana", "password")
|
||||||
|
|
||||||
|
// Create rule under folder2
|
||||||
|
createRule(t, grafanaListedAddr, "folder2", "grafana", "password")
|
||||||
|
|
||||||
|
// Now, let's see how this looks like.
|
||||||
|
{
|
||||||
|
promRulesURL := fmt.Sprintf("http://grafana:password@%s/api/prometheus/grafana/api/v1/rules", grafanaListedAddr)
|
||||||
|
// nolint:gosec
|
||||||
|
resp, err := http.Get(promRulesURL)
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
err := resp.Body.Close()
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
b, err := ioutil.ReadAll(resp.Body)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 200, resp.StatusCode)
|
||||||
|
|
||||||
|
require.JSONEq(t, `
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"data": {
|
||||||
|
"groups": [{
|
||||||
|
"name": "arulegroup",
|
||||||
|
"file": "folder1",
|
||||||
|
"rules": [{
|
||||||
|
"state": "inactive",
|
||||||
|
"name": "rule under folder folder1",
|
||||||
|
"query": "[{\"refId\":\"A\",\"queryType\":\"\",\"relativeTimeRange\":{\"from\":18000,\"to\":10800},\"datasourceUid\":\"-100\",\"model\":{\"expression\":\"2 + 3 \\u003e 1\",\"intervalMs\":1000,\"maxDataPoints\":43200,\"type\":\"math\"}}]",
|
||||||
|
"duration": 120,
|
||||||
|
"annotations": {
|
||||||
|
"annotation1": "val1"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"label1": "val1"
|
||||||
|
},
|
||||||
|
"health": "ok",
|
||||||
|
"lastError": "",
|
||||||
|
"type": "alerting",
|
||||||
|
"lastEvaluation": "0001-01-01T00:00:00Z",
|
||||||
|
"evaluationTime": 0
|
||||||
|
}],
|
||||||
|
"interval": 60,
|
||||||
|
"lastEvaluation": "0001-01-01T00:00:00Z",
|
||||||
|
"evaluationTime": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "arulegroup",
|
||||||
|
"file": "folder2",
|
||||||
|
"rules": [{
|
||||||
|
"state": "inactive",
|
||||||
|
"name": "rule under folder folder2",
|
||||||
|
"query": "[{\"refId\":\"A\",\"queryType\":\"\",\"relativeTimeRange\":{\"from\":18000,\"to\":10800},\"datasourceUid\":\"-100\",\"model\":{\"expression\":\"2 + 3 \\u003e 1\",\"intervalMs\":1000,\"maxDataPoints\":43200,\"type\":\"math\"}}]",
|
||||||
|
"duration": 120,
|
||||||
|
"annotations": {
|
||||||
|
"annotation1": "val1"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"label1": "val1"
|
||||||
|
},
|
||||||
|
"health": "ok",
|
||||||
|
"lastError": "",
|
||||||
|
"type": "alerting",
|
||||||
|
"lastEvaluation": "0001-01-01T00:00:00Z",
|
||||||
|
"evaluationTime": 0
|
||||||
|
}],
|
||||||
|
"interval": 60,
|
||||||
|
"lastEvaluation": "0001-01-01T00:00:00Z",
|
||||||
|
"evaluationTime": 0
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}`, string(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove permissions from folder2
|
||||||
|
require.NoError(t, store.UpdateDashboardACL(2, nil))
|
||||||
|
|
||||||
|
// make sure that folder2 is not included in the response
|
||||||
|
{
|
||||||
|
promRulesURL := fmt.Sprintf("http://grafana:password@%s/api/prometheus/grafana/api/v1/rules", grafanaListedAddr)
|
||||||
|
// nolint:gosec
|
||||||
|
resp, err := http.Get(promRulesURL)
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
err := resp.Body.Close()
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
b, err := ioutil.ReadAll(resp.Body)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 200, resp.StatusCode)
|
||||||
|
|
||||||
|
require.JSONEq(t, `
|
||||||
|
{
|
||||||
|
"status": "success",
|
||||||
|
"data": {
|
||||||
|
"groups": [{
|
||||||
|
"name": "arulegroup",
|
||||||
|
"file": "folder1",
|
||||||
|
"rules": [{
|
||||||
|
"state": "inactive",
|
||||||
|
"name": "rule under folder folder1",
|
||||||
|
"query": "[{\"refId\":\"A\",\"queryType\":\"\",\"relativeTimeRange\":{\"from\":18000,\"to\":10800},\"datasourceUid\":\"-100\",\"model\":{\"expression\":\"2 + 3 \\u003e 1\",\"intervalMs\":1000,\"maxDataPoints\":43200,\"type\":\"math\"}}]",
|
||||||
|
"duration": 120,
|
||||||
|
"annotations": {
|
||||||
|
"annotation1": "val1"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"label1": "val1"
|
||||||
|
},
|
||||||
|
"health": "ok",
|
||||||
|
"lastError": "",
|
||||||
|
"type": "alerting",
|
||||||
|
"lastEvaluation": "0001-01-01T00:00:00Z",
|
||||||
|
"evaluationTime": 0
|
||||||
|
}],
|
||||||
|
"interval": 60,
|
||||||
|
"lastEvaluation": "0001-01-01T00:00:00Z",
|
||||||
|
"evaluationTime": 0
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}`, string(b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user