mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Update authorization rules for RouteGetNamespaceRulesConfig (#50965)
* use authorizeAccessToRuleGroup * use toGettableRuleGroupConfig in get by namespace * add comments for controller methods
This commit is contained in:
parent
4489f331b8
commit
81089b956a
@ -157,6 +157,7 @@ func (srv RulerSrv) RouteDeleteAlertRules(c *models.ReqContext) response.Respons
|
||||
return response.JSON(http.StatusAccepted, util.DynMap{"message": "rules deleted"})
|
||||
}
|
||||
|
||||
// RouteGetNamespaceRulesConfig returns all rules in a specific folder that user has access to
|
||||
func (srv RulerSrv) RouteGetNamespaceRulesConfig(c *models.ReqContext) response.Response {
|
||||
namespaceTitle := web.Params(c.Req)[":Namespace"]
|
||||
namespace, err := srv.store.GetNamespaceByTitle(c.Req.Context(), namespaceTitle, c.SignedInUser.OrgId, c.SignedInUser, false)
|
||||
@ -173,7 +174,6 @@ func (srv RulerSrv) RouteGetNamespaceRulesConfig(c *models.ReqContext) response.
|
||||
}
|
||||
|
||||
result := apimodels.NamespaceConfigResponse{}
|
||||
ruleGroupConfigs := make(map[string]apimodels.GettableRuleGroupConfig)
|
||||
|
||||
hasAccess := func(evaluator accesscontrol.Evaluator) bool {
|
||||
return accesscontrol.HasAccess(srv.ac, c)(accesscontrol.ReqViewer, evaluator)
|
||||
@ -184,33 +184,23 @@ func (srv RulerSrv) RouteGetNamespaceRulesConfig(c *models.ReqContext) response.
|
||||
return ErrResp(http.StatusInternalServerError, err, "failed to get provenance for rule group")
|
||||
}
|
||||
|
||||
ruleGroups := make(map[string][]*ngmodels.AlertRule)
|
||||
for _, r := range q.Result {
|
||||
if !authorizeDatasourceAccessForRule(r, hasAccess) {
|
||||
continue
|
||||
}
|
||||
ruleGroupConfig, ok := ruleGroupConfigs[r.RuleGroup]
|
||||
if !ok {
|
||||
ruleGroupInterval := model.Duration(time.Duration(r.IntervalSeconds) * time.Second)
|
||||
ruleGroupConfigs[r.RuleGroup] = apimodels.GettableRuleGroupConfig{
|
||||
Name: r.RuleGroup,
|
||||
Interval: ruleGroupInterval,
|
||||
Rules: []apimodels.GettableExtendedRuleNode{
|
||||
toGettableExtendedRuleNode(*r, namespace.Id, provenanceRecords),
|
||||
},
|
||||
}
|
||||
} else {
|
||||
ruleGroupConfig.Rules = append(ruleGroupConfig.Rules, toGettableExtendedRuleNode(*r, namespace.Id, provenanceRecords))
|
||||
ruleGroupConfigs[r.RuleGroup] = ruleGroupConfig
|
||||
}
|
||||
ruleGroups[r.RuleGroup] = append(ruleGroups[r.RuleGroup], r)
|
||||
}
|
||||
|
||||
for _, ruleGroupConfig := range ruleGroupConfigs {
|
||||
result[namespaceTitle] = append(result[namespaceTitle], ruleGroupConfig)
|
||||
for groupName, rules := range ruleGroups {
|
||||
if !authorizeAccessToRuleGroup(rules, hasAccess) {
|
||||
continue
|
||||
}
|
||||
result[namespaceTitle] = append(result[namespaceTitle], toGettableRuleGroupConfig(groupName, rules, namespace.Id, provenanceRecords))
|
||||
}
|
||||
|
||||
return response.JSON(http.StatusAccepted, result)
|
||||
}
|
||||
|
||||
// RouteGetRulesGroupConfig returns rules that belong to a specific group in a specific namespace (folder).
|
||||
// If user does not have access to at least one of the rule in the group, returns status 401 Unauthorized
|
||||
func (srv RulerSrv) RouteGetRulesGroupConfig(c *models.ReqContext) response.Response {
|
||||
namespaceTitle := web.Params(c.Req)[":Namespace"]
|
||||
namespace, err := srv.store.GetNamespaceByTitle(c.Req.Context(), namespaceTitle, c.SignedInUser.OrgId, c.SignedInUser, false)
|
||||
@ -237,20 +227,17 @@ func (srv RulerSrv) RouteGetRulesGroupConfig(c *models.ReqContext) response.Resp
|
||||
return ErrResp(http.StatusInternalServerError, err, "failed to get group alert rules")
|
||||
}
|
||||
|
||||
groupRules := make([]*ngmodels.AlertRule, 0, len(q.Result))
|
||||
for _, r := range q.Result {
|
||||
if !authorizeDatasourceAccessForRule(r, hasAccess) {
|
||||
return ErrResp(http.StatusUnauthorized, fmt.Errorf("%w to access the group because it does not have access to one or many data sources one or many rules in the group use", ErrAuthorization), "")
|
||||
}
|
||||
groupRules = append(groupRules, r)
|
||||
if !authorizeAccessToRuleGroup(q.Result, hasAccess) {
|
||||
return ErrResp(http.StatusUnauthorized, fmt.Errorf("%w to access the group because it does not have access to one or many data sources one or many rules in the group use", ErrAuthorization), "")
|
||||
}
|
||||
|
||||
result := apimodels.RuleGroupConfigResponse{
|
||||
GettableRuleGroupConfig: toGettableRuleGroupConfig(ruleGroup, groupRules, namespace.Id, provenanceRecords),
|
||||
GettableRuleGroupConfig: toGettableRuleGroupConfig(ruleGroup, q.Result, namespace.Id, provenanceRecords),
|
||||
}
|
||||
return response.JSON(http.StatusAccepted, result)
|
||||
}
|
||||
|
||||
// RouteGetRulesConfig returns all alert rules that are available to the current user
|
||||
func (srv RulerSrv) RouteGetRulesConfig(c *models.ReqContext) response.Response {
|
||||
namespaceMap, err := srv.store.GetUserVisibleNamespaces(c.Req.Context(), c.OrgId, c.SignedInUser)
|
||||
if err != nil {
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -659,6 +660,84 @@ func TestRouteGetNamespaceRulesConfig(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestRouteGetRulesConfig(t *testing.T) {
|
||||
t.Run("fine-grained access is enabled", func(t *testing.T) {
|
||||
t.Run("should check access to data source", func(t *testing.T) {
|
||||
orgID := rand.Int63()
|
||||
ruleStore := store.NewFakeRuleStore(t)
|
||||
folder1 := randFolder()
|
||||
folder2 := randFolder()
|
||||
ruleStore.Folders[orgID] = []*models2.Folder{folder1, folder2}
|
||||
|
||||
group1Key := models.GenerateGroupKey(orgID)
|
||||
group1Key.NamespaceUID = folder1.Uid
|
||||
group2Key := models.GenerateGroupKey(orgID)
|
||||
group2Key.NamespaceUID = folder2.Uid
|
||||
|
||||
group1 := models.GenerateAlertRules(rand.Intn(4)+2, models.AlertRuleGen(withGroupKey(group1Key)))
|
||||
group2 := models.GenerateAlertRules(rand.Intn(4)+2, models.AlertRuleGen(withGroupKey(group2Key)))
|
||||
ruleStore.PutRule(context.Background(), append(group1, group2...)...)
|
||||
|
||||
request := createRequestContext(orgID, "", nil)
|
||||
t.Run("and do not return group if user does not have access to one of rules", func(t *testing.T) {
|
||||
ac := acMock.New().WithPermissions(createPermissionsForRules(append(group1, group2[1:]...)))
|
||||
response := createService(ac, ruleStore, nil).RouteGetRulesConfig(request)
|
||||
require.Equal(t, http.StatusOK, response.Status())
|
||||
|
||||
result := &apimodels.NamespaceConfigResponse{}
|
||||
require.NoError(t, json.Unmarshal(response.Body(), result))
|
||||
require.NotNil(t, result)
|
||||
|
||||
require.Contains(t, *result, folder1.Title)
|
||||
require.NotContains(t, *result, folder2.Title)
|
||||
|
||||
groups := (*result)[folder1.Title]
|
||||
require.Len(t, groups, 1)
|
||||
require.Equal(t, group1Key.RuleGroup, groups[0].Name)
|
||||
require.Len(t, groups[0].Rules, len(group1))
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestRouteGetRulesGroupConfig(t *testing.T) {
|
||||
t.Run("fine-grained access is enabled", func(t *testing.T) {
|
||||
t.Run("should check access to data source", func(t *testing.T) {
|
||||
orgID := rand.Int63()
|
||||
folder := randFolder()
|
||||
ruleStore := store.NewFakeRuleStore(t)
|
||||
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder)
|
||||
groupKey := models.GenerateGroupKey(orgID)
|
||||
groupKey.NamespaceUID = folder.Uid
|
||||
|
||||
expectedRules := models.GenerateAlertRules(rand.Intn(4)+2, models.AlertRuleGen(withGroupKey(groupKey)))
|
||||
ruleStore.PutRule(context.Background(), expectedRules...)
|
||||
|
||||
request := createRequestContext(orgID, "", map[string]string{
|
||||
":Namespace": folder.Title,
|
||||
":Groupname": groupKey.RuleGroup,
|
||||
})
|
||||
|
||||
t.Run("and return 401 if user does not have access one of rules", func(t *testing.T) {
|
||||
ac := acMock.New().WithPermissions(createPermissionsForRules(expectedRules[1:]))
|
||||
response := createService(ac, ruleStore, nil).RouteGetRulesGroupConfig(request)
|
||||
require.Equal(t, http.StatusUnauthorized, response.Status())
|
||||
})
|
||||
|
||||
t.Run("and return rules if user has access to all of them", func(t *testing.T) {
|
||||
ac := acMock.New().WithPermissions(createPermissionsForRules(expectedRules))
|
||||
response := createService(ac, ruleStore, nil).RouteGetRulesGroupConfig(request)
|
||||
|
||||
require.Equal(t, http.StatusAccepted, response.Status())
|
||||
result := &apimodels.RuleGroupConfigResponse{}
|
||||
require.NoError(t, json.Unmarshal(response.Body(), result))
|
||||
require.NotNil(t, result)
|
||||
require.Len(t, result.Rules, len(expectedRules))
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestVerifyProvisionedRulesNotAffected(t *testing.T) {
|
||||
orgID := rand.Int63()
|
||||
group := models.GenerateGroupKey(orgID)
|
||||
@ -735,7 +814,10 @@ func createService(ac *acMock.Mock, store *store.FakeRuleStore, scheduler schedu
|
||||
}
|
||||
|
||||
func createRequestContext(orgID int64, role models2.RoleType, params map[string]string) *models2.ReqContext {
|
||||
ctx := web.Context{Req: &http.Request{}}
|
||||
uri, _ := url.Parse("http://localhost")
|
||||
ctx := web.Context{Req: &http.Request{
|
||||
URL: uri,
|
||||
}}
|
||||
ctx.Req = web.SetURLParams(ctx.Req, params)
|
||||
|
||||
return &models2.ReqContext{
|
||||
|
Loading…
Reference in New Issue
Block a user