Alerting: Fix anonymous access to alerting (#49203) (#49268)

* introduce a fallback handler that checks that role is Viewer.
* update UI nav links to allow alerting tabs for anonymous user
* update rule api to check for Viewer role instead of SignedIn when RBAC is disabled

(cherry picked from commit f7f2253072)
This commit is contained in:
Yuriy Tseretyan
2022-05-19 12:33:29 -04:00
committed by GitHub
parent 2a8c8280b8
commit 1547e279b1
8 changed files with 33 additions and 12 deletions
+2 -2
View File
@@ -515,7 +515,7 @@ func (hs *HTTPServer) buildAlertNavLinks(c *models.ReqContext) []*dtos.NavLink {
hasAccess := ac.HasAccess(hs.AccessControl, c)
var alertChildNavs []*dtos.NavLink
if hasAccess(ac.ReqSignedIn, ac.EvalAny(ac.EvalPermission(ac.ActionAlertingRuleRead), ac.EvalPermission(ac.ActionAlertingRuleExternalRead))) {
if hasAccess(ac.ReqViewer, ac.EvalAny(ac.EvalPermission(ac.ActionAlertingRuleRead), ac.EvalPermission(ac.ActionAlertingRuleExternalRead))) {
alertChildNavs = append(alertChildNavs, &dtos.NavLink{
Text: "Alert rules", Id: "alert-list", Url: hs.Cfg.AppSubURL + "/alerting/list", Icon: "list-ul",
})
@@ -529,7 +529,7 @@ func (hs *HTTPServer) buildAlertNavLinks(c *models.ReqContext) []*dtos.NavLink {
alertChildNavs = append(alertChildNavs, &dtos.NavLink{Text: "Notification policies", Id: "am-routes", Url: hs.Cfg.AppSubURL + "/alerting/routes", Icon: "sitemap"})
}
if hasAccess(ac.ReqSignedIn, ac.EvalAny(ac.EvalPermission(ac.ActionAlertingInstanceRead), ac.EvalPermission(ac.ActionAlertingInstancesExternalRead))) {
if hasAccess(ac.ReqViewer, ac.EvalAny(ac.EvalPermission(ac.ActionAlertingInstanceRead), ac.EvalPermission(ac.ActionAlertingInstancesExternalRead))) {
alertChildNavs = append(alertChildNavs, &dtos.NavLink{Text: "Silences", Id: "silences", Url: hs.Cfg.AppSubURL + "/alerting/silences", Icon: "bell-slash"})
alertChildNavs = append(alertChildNavs, &dtos.NavLink{Text: "Alert groups", Id: "groups", Url: hs.Cfg.AppSubURL + "/alerting/groups", Icon: "layer-group"})
}
@@ -116,6 +116,11 @@ var ReqGrafanaAdmin = func(c *models.ReqContext) bool {
return c.IsGrafanaAdmin
}
// ReqViewer returns true if the current user has models.ROLE_VIEWER. Note: this can be anonymous user as well
var ReqViewer = func(c *models.ReqContext) bool {
return c.OrgRole.Includes(models.ROLE_VIEWER)
}
var ReqOrgAdmin = func(c *models.ReqContext) bool {
return c.OrgRole == models.ROLE_ADMIN
}
+17 -1
View File
@@ -45,11 +45,26 @@ Scopes must have an order to ensure consistency and ease of search, this helps u
## Grafana Alerting - main / unreleased
- [BUGFIX] Use `NaN` and do not panic when captured alert values are empty #48370
- [ENHANCEMENT] Scheduler: Ticker expose new metrics. In legacy, metrics are prefixed with `legacy_` #47828, #48190
- `grafana_alerting_ticker_last_consumed_tick_timestamp_seconds`
- `grafana_alerting_ticker_next_tick_timestamp_seconds`
- `grafana_alerting_ticker_interval_seconds`
- [ENHANCEMENT] Create folder 'General Alerting' when Grafana starts from the scratch #48866
- [ENHANCEMENT] Rule changes authorization logic to use UID folder scope instead of ID scope #48970
- [FEATURE] Indicate whether routes are provisioned when GETting Alertmanager configuration #47857
- [FEATURE] Indicate whether contact point is provisioned when GETting Alertmanager configuration #48323
- [FEATURE] Indicate whether alert rule is provisioned when GETting the rule #48458
- [BUGFIX] Migration: ignore alerts that do not belong to any existing organization\dashboard #49192
- [BUGFIX] Allow anonymous access to alerts #49203
## 8.5.3
- [BUGFIX] Migration: Remove data source disabled property when migrating alerts #48559
## 8.5.2
- [FEATURE] Migration: Adds `force_migration` as a flag to prevent truncating the unified alerting tables as we migrate. #48526
- [BUGFIX] Use `NaN` and do not panic when captured alert values are empty #48370
## 8.5.1
@@ -63,3 +78,4 @@ Scopes must have an order to ensure consistency and ease of search, this helps u
- [BUGFIX] (Legacy) Templates: Parse notification templates using all the matches of the alert rule when going from `Alerting` to `OK` in legacy alerting #47355
- [BUGFIX] Scheduler: Fix state manager to support OK option of `AlertRule.ExecErrState` #47670
- [ENHANCEMENT] Templates: Enable the use of classic condition values in templates #46971
+1 -1
View File
@@ -154,7 +154,7 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *models.ReqContext) response.Res
return response.JSON(http.StatusInternalServerError, ruleResponse)
}
hasAccess := func(evaluator accesscontrol.Evaluator) bool {
return accesscontrol.HasAccess(srv.ac, c)(accesscontrol.ReqSignedIn, evaluator)
return accesscontrol.HasAccess(srv.ac, c)(accesscontrol.ReqViewer, evaluator)
}
groupMap := make(map[string]*apimodels.RuleGroup)
@@ -255,7 +255,7 @@ func TestRouteGetRuleStatuses(t *testing.T) {
req, err := http.NewRequest("GET", "/api/v1/rules", nil)
require.NoError(t, err)
c := &models.ReqContext{Context: &web.Context{Req: req}, SignedInUser: &models.SignedInUser{OrgId: orgID}, IsSignedIn: true}
c := &models.ReqContext{Context: &web.Context{Req: req}, SignedInUser: &models.SignedInUser{OrgId: orgID, OrgRole: models.ROLE_VIEWER}}
t.Run("with no rules", func(t *testing.T) {
_, _, _, api := setupAPI(t)
@@ -325,7 +325,7 @@ func TestRouteGetRuleStatuses(t *testing.T) {
req, err := http.NewRequest("GET", "/api/v1/rules?includeInternalLabels=true", nil)
require.NoError(t, err)
c := &models.ReqContext{Context: &web.Context{Req: req}, SignedInUser: &models.SignedInUser{OrgId: orgID}, IsSignedIn: true}
c := &models.ReqContext{Context: &web.Context{Req: req}, SignedInUser: &models.SignedInUser{OrgId: orgID, OrgRole: models.ROLE_VIEWER}}
r := api.RouteGetRuleStatuses(c)
require.Equal(t, http.StatusOK, r.Status())
+3 -3
View File
@@ -174,7 +174,7 @@ func (srv RulerSrv) RouteGetNamespaceRulesConfig(c *models.ReqContext) response.
ruleGroupConfigs := make(map[string]apimodels.GettableRuleGroupConfig)
hasAccess := func(evaluator accesscontrol.Evaluator) bool {
return accesscontrol.HasAccess(srv.ac, c)(accesscontrol.ReqSignedIn, evaluator)
return accesscontrol.HasAccess(srv.ac, c)(accesscontrol.ReqViewer, evaluator)
}
provenanceRecords, err := srv.provenanceStore.GetProvenances(c.Req.Context(), c.SignedInUser.OrgId, (&ngmodels.AlertRule{}).ResourceType())
@@ -230,7 +230,7 @@ func (srv RulerSrv) RouteGetRulesGroupConfig(c *models.ReqContext) response.Resp
ruleNodes := make([]apimodels.GettableExtendedRuleNode, 0, len(q.Result))
hasAccess := func(evaluator accesscontrol.Evaluator) bool {
return accesscontrol.HasAccess(srv.ac, c)(accesscontrol.ReqSignedIn, evaluator)
return accesscontrol.HasAccess(srv.ac, c)(accesscontrol.ReqViewer, evaluator)
}
provenanceRecords, err := srv.provenanceStore.GetProvenances(c.Req.Context(), c.SignedInUser.OrgId, (&ngmodels.AlertRule{}).ResourceType())
@@ -296,7 +296,7 @@ func (srv RulerSrv) RouteGetRulesConfig(c *models.ReqContext) response.Response
configs := make(map[string]map[string]apimodels.GettableRuleGroupConfig)
hasAccess := func(evaluator accesscontrol.Evaluator) bool {
return accesscontrol.HasAccess(srv.ac, c)(accesscontrol.ReqSignedIn, evaluator)
return accesscontrol.HasAccess(srv.ac, c)(accesscontrol.ReqViewer, evaluator)
}
provenanceRecords, err := srv.provenanceStore.GetProvenances(c.Req.Context(), c.SignedInUser.OrgId, (&ngmodels.AlertRule{}).ResourceType())
+2 -2
View File
@@ -577,7 +577,7 @@ func TestRouteGetNamespaceRulesConfig(t *testing.T) {
ruleStore.PutRule(context.Background(), expectedRules...)
ac := acMock.New().WithDisabled()
response := createService(ac, ruleStore, nil).RouteGetNamespaceRulesConfig(createRequestContext(orgID, "", map[string]string{
response := createService(ac, ruleStore, nil).RouteGetNamespaceRulesConfig(createRequestContext(orgID, models2.ROLE_VIEWER, map[string]string{
":Namespace": folder.Title,
}))
@@ -621,7 +621,7 @@ func TestRouteGetNamespaceRulesConfig(t *testing.T) {
err := svc.provenanceStore.SetProvenance(context.Background(), rule, orgID, models.ProvenanceAPI)
require.NoError(t, err)
response := svc.RouteGetNamespaceRulesConfig(createRequestContext(orgID, "", map[string]string{
response := svc.RouteGetNamespaceRulesConfig(createRequestContext(orgID, models2.ROLE_VIEWER, map[string]string{
":Namespace": folder.Title,
}))
+1 -1
View File
@@ -134,7 +134,7 @@ const unifiedRoutes: RouteDescriptor[] = [
path: '/alerting/silences',
roles: evaluateAccess(
[AccessControlAction.AlertingInstanceRead, AccessControlAction.AlertingInstancesExternalRead],
['Editor', 'Admin']
['Viewer', 'Editor', 'Admin']
),
component: SafeDynamicImport(
() => import(/* webpackChunkName: "AlertSilences" */ 'app/features/alerting/unified/Silences')