mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
NGAlerting: Use identity.Requester interface instead of SignedInUser (#76360)
* unfurl SignedInUserAttrs services * replace signedInUser with Requester replace signedInUser with requester * fix tests * linting --------- Co-authored-by: Ieva <ieva.vasiljeva@grafana.com>
This commit is contained in:
@@ -44,7 +44,7 @@ func (srv ConfigSrv) RouteGetAlertmanagers(c *contextmodel.ReqContext) response.
|
||||
}
|
||||
|
||||
func (srv ConfigSrv) RouteGetNGalertConfig(c *contextmodel.ReqContext) response.Response {
|
||||
if c.OrgRole != org.RoleAdmin {
|
||||
if c.SignedInUser.GetOrgRole() != org.RoleAdmin {
|
||||
return accessForbiddenResp()
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ func (srv ConfigSrv) RouteGetNGalertConfig(c *contextmodel.ReqContext) response.
|
||||
}
|
||||
|
||||
func (srv ConfigSrv) RoutePostNGalertConfig(c *contextmodel.ReqContext, body apimodels.PostableNGalertConfig) response.Response {
|
||||
if c.OrgRole != org.RoleAdmin {
|
||||
if c.SignedInUser.GetOrgRole() != org.RoleAdmin {
|
||||
return accessForbiddenResp()
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ func (srv ConfigSrv) RoutePostNGalertConfig(c *contextmodel.ReqContext, body api
|
||||
}
|
||||
|
||||
func (srv ConfigSrv) RouteDeleteNGalertConfig(c *contextmodel.ReqContext) response.Response {
|
||||
if c.OrgRole != org.RoleAdmin {
|
||||
if c.SignedInUser.GetOrgRole() != org.RoleAdmin {
|
||||
return accessForbiddenResp()
|
||||
}
|
||||
|
||||
|
||||
@@ -201,7 +201,7 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *contextmodel.ReqContext) respon
|
||||
}
|
||||
|
||||
alertRuleQuery := ngmodels.ListAlertRulesQuery{
|
||||
OrgID: c.SignedInUser.OrgID,
|
||||
OrgID: c.SignedInUser.GetOrgID(),
|
||||
NamespaceUIDs: namespaceUIDs,
|
||||
DashboardUID: dashboardUID,
|
||||
PanelID: panelID,
|
||||
|
||||
@@ -9,13 +9,13 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/hcl"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
alerting_models "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/user"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
@@ -31,7 +31,7 @@ type ProvisioningSrv struct {
|
||||
}
|
||||
|
||||
type ContactPointService interface {
|
||||
GetContactPoints(ctx context.Context, q provisioning.ContactPointQuery, user *user.SignedInUser) ([]definitions.EmbeddedContactPoint, error)
|
||||
GetContactPoints(ctx context.Context, q provisioning.ContactPointQuery, user identity.Requester) ([]definitions.EmbeddedContactPoint, error)
|
||||
CreateContactPoint(ctx context.Context, orgID int64, contactPoint definitions.EmbeddedContactPoint, p alerting_models.Provenance) (definitions.EmbeddedContactPoint, error)
|
||||
UpdateContactPoint(ctx context.Context, orgID int64, contactPoint definitions.EmbeddedContactPoint, p alerting_models.Provenance) error
|
||||
DeleteContactPoint(ctx context.Context, orgID int64, uid string) error
|
||||
@@ -325,7 +325,8 @@ func (srv *ProvisioningSrv) RoutePostAlertRule(c *contextmodel.ReqContext, ar de
|
||||
return ErrResp(http.StatusBadRequest, err, "")
|
||||
}
|
||||
provenance := determineProvenance(c)
|
||||
createdAlertRule, err := srv.alertRules.CreateAlertRule(c.Req.Context(), upstreamModel, alerting_models.Provenance(provenance), c.UserID)
|
||||
userID, _ := identity.UserIdentifier(c.SignedInUser.GetNamespacedID())
|
||||
createdAlertRule, err := srv.alertRules.CreateAlertRule(c.Req.Context(), upstreamModel, alerting_models.Provenance(provenance), userID)
|
||||
if errors.Is(err, alerting_models.ErrAlertRuleFailedValidation) {
|
||||
return ErrResp(http.StatusBadRequest, err, "")
|
||||
}
|
||||
@@ -478,7 +479,9 @@ func (srv *ProvisioningSrv) RoutePutAlertRuleGroup(c *contextmodel.ReqContext, a
|
||||
ErrResp(http.StatusBadRequest, err, "")
|
||||
}
|
||||
provenance := determineProvenance(c)
|
||||
err = srv.alertRules.ReplaceRuleGroup(c.Req.Context(), c.SignedInUser.GetOrgID(), groupModel, c.UserID, alerting_models.Provenance(provenance))
|
||||
|
||||
userID, _ := identity.UserIdentifier(c.SignedInUser.GetNamespacedID())
|
||||
err = srv.alertRules.ReplaceRuleGroup(c.Req.Context(), c.SignedInUser.GetOrgID(), groupModel, userID, alerting_models.Provenance(provenance))
|
||||
if errors.Is(err, alerting_models.ErrAlertRuleUniqueConstraintViolation) {
|
||||
return ErrResp(http.StatusBadRequest, err, "")
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
@@ -22,7 +23,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
"github.com/grafana/grafana/pkg/services/quota"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
@@ -52,14 +52,17 @@ var (
|
||||
// Returns http.StatusUnauthorized if user does not have access to any of the rules that match the filter.
|
||||
// Returns http.StatusBadRequest if all rules that match the filter and the user is authorized to delete are provisioned.
|
||||
func (srv RulerSrv) RouteDeleteAlertRules(c *contextmodel.ReqContext, namespaceTitle string, group string) response.Response {
|
||||
namespace, err := srv.store.GetNamespaceByTitle(c.Req.Context(), namespaceTitle, c.SignedInUser.OrgID, c.SignedInUser)
|
||||
namespace, err := srv.store.GetNamespaceByTitle(c.Req.Context(), namespaceTitle, c.SignedInUser.GetOrgID(), c.SignedInUser)
|
||||
if err != nil {
|
||||
return toNamespaceErrorResponse(err)
|
||||
}
|
||||
|
||||
userNamespace, id := c.SignedInUser.GetNamespacedID()
|
||||
var loggerCtx = []any{
|
||||
"userId",
|
||||
c.SignedInUser.UserID,
|
||||
id,
|
||||
"userNamespace",
|
||||
userNamespace,
|
||||
"namespaceUid",
|
||||
namespace.UID,
|
||||
}
|
||||
@@ -68,7 +71,7 @@ func (srv RulerSrv) RouteDeleteAlertRules(c *contextmodel.ReqContext, namespaceT
|
||||
}
|
||||
logger := srv.log.New(loggerCtx...)
|
||||
|
||||
provenances, err := srv.provenanceStore.GetProvenances(c.Req.Context(), c.SignedInUser.OrgID, (&ngmodels.AlertRule{}).ResourceType())
|
||||
provenances, err := srv.provenanceStore.GetProvenances(c.Req.Context(), c.SignedInUser.GetOrgID(), (&ngmodels.AlertRule{}).ResourceType())
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusInternalServerError, err, "failed to fetch provenances of alert rules")
|
||||
}
|
||||
@@ -111,7 +114,7 @@ func (srv RulerSrv) RouteDeleteAlertRules(c *contextmodel.ReqContext, namespaceT
|
||||
rulesToDelete = append(rulesToDelete, uid...)
|
||||
}
|
||||
if len(rulesToDelete) > 0 {
|
||||
err := srv.store.DeleteAlertRulesByUID(ctx, c.SignedInUser.OrgID, rulesToDelete...)
|
||||
err := srv.store.DeleteAlertRulesByUID(ctx, c.SignedInUser.GetOrgID(), rulesToDelete...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -141,7 +144,7 @@ func (srv RulerSrv) RouteDeleteAlertRules(c *contextmodel.ReqContext, namespaceT
|
||||
|
||||
// RouteGetNamespaceRulesConfig returns all rules in a specific folder that user has access to
|
||||
func (srv RulerSrv) RouteGetNamespaceRulesConfig(c *contextmodel.ReqContext, namespaceTitle string) response.Response {
|
||||
namespace, err := srv.store.GetNamespaceByTitle(c.Req.Context(), namespaceTitle, c.SignedInUser.OrgID, c.SignedInUser)
|
||||
namespace, err := srv.store.GetNamespaceByTitle(c.Req.Context(), namespaceTitle, c.SignedInUser.GetOrgID(), c.SignedInUser)
|
||||
if err != nil {
|
||||
return toNamespaceErrorResponse(err)
|
||||
}
|
||||
@@ -150,7 +153,7 @@ func (srv RulerSrv) RouteGetNamespaceRulesConfig(c *contextmodel.ReqContext, nam
|
||||
if err != nil {
|
||||
return errorToResponse(err)
|
||||
}
|
||||
provenanceRecords, err := srv.provenanceStore.GetProvenances(c.Req.Context(), c.SignedInUser.OrgID, (&ngmodels.AlertRule{}).ResourceType())
|
||||
provenanceRecords, err := srv.provenanceStore.GetProvenances(c.Req.Context(), c.SignedInUser.GetOrgID(), (&ngmodels.AlertRule{}).ResourceType())
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusInternalServerError, err, "failed to get provenance for rule group")
|
||||
}
|
||||
@@ -167,7 +170,7 @@ func (srv RulerSrv) RouteGetNamespaceRulesConfig(c *contextmodel.ReqContext, nam
|
||||
// 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 *contextmodel.ReqContext, namespaceTitle string, ruleGroup string) response.Response {
|
||||
namespace, err := srv.store.GetNamespaceByTitle(c.Req.Context(), namespaceTitle, c.SignedInUser.OrgID, c.SignedInUser)
|
||||
namespace, err := srv.store.GetNamespaceByTitle(c.Req.Context(), namespaceTitle, c.SignedInUser.GetOrgID(), c.SignedInUser)
|
||||
if err != nil {
|
||||
return toNamespaceErrorResponse(err)
|
||||
}
|
||||
@@ -181,7 +184,7 @@ func (srv RulerSrv) RouteGetRulesGroupConfig(c *contextmodel.ReqContext, namespa
|
||||
return errorToResponse(err)
|
||||
}
|
||||
|
||||
provenanceRecords, err := srv.provenanceStore.GetProvenances(c.Req.Context(), c.SignedInUser.OrgID, (&ngmodels.AlertRule{}).ResourceType())
|
||||
provenanceRecords, err := srv.provenanceStore.GetProvenances(c.Req.Context(), c.SignedInUser.GetOrgID(), (&ngmodels.AlertRule{}).ResourceType())
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusInternalServerError, err, "failed to get group alert rules")
|
||||
}
|
||||
@@ -223,7 +226,7 @@ func (srv RulerSrv) RouteGetRulesConfig(c *contextmodel.ReqContext) response.Res
|
||||
if err != nil {
|
||||
return errorToResponse(err)
|
||||
}
|
||||
provenanceRecords, err := srv.provenanceStore.GetProvenances(c.Req.Context(), c.SignedInUser.OrgID, (&ngmodels.AlertRule{}).ResourceType())
|
||||
provenanceRecords, err := srv.provenanceStore.GetProvenances(c.Req.Context(), c.SignedInUser.GetOrgID(), (&ngmodels.AlertRule{}).ResourceType())
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusInternalServerError, err, "failed to get alert rules")
|
||||
}
|
||||
@@ -231,7 +234,8 @@ func (srv RulerSrv) RouteGetRulesConfig(c *contextmodel.ReqContext) response.Res
|
||||
for groupKey, rules := range configs {
|
||||
folder, ok := namespaceMap[groupKey.NamespaceUID]
|
||||
if !ok {
|
||||
srv.log.Error("Namespace not visible to the user", "user", c.SignedInUser.UserID, "namespace", groupKey.NamespaceUID)
|
||||
userNamespace, id := c.SignedInUser.GetNamespacedID()
|
||||
srv.log.Error("Namespace not visible to the user", "user", id, "userNamespace", userNamespace, "namespace", groupKey.NamespaceUID)
|
||||
continue
|
||||
}
|
||||
namespace := folder.Title
|
||||
@@ -241,18 +245,18 @@ func (srv RulerSrv) RouteGetRulesConfig(c *contextmodel.ReqContext) response.Res
|
||||
}
|
||||
|
||||
func (srv RulerSrv) RoutePostNameRulesConfig(c *contextmodel.ReqContext, ruleGroupConfig apimodels.PostableRuleGroupConfig, namespaceTitle string) response.Response {
|
||||
namespace, err := srv.store.GetNamespaceByTitle(c.Req.Context(), namespaceTitle, c.SignedInUser.OrgID, c.SignedInUser)
|
||||
namespace, err := srv.store.GetNamespaceByTitle(c.Req.Context(), namespaceTitle, c.SignedInUser.GetOrgID(), c.SignedInUser)
|
||||
if err != nil {
|
||||
return toNamespaceErrorResponse(err)
|
||||
}
|
||||
|
||||
rules, err := validateRuleGroup(&ruleGroupConfig, c.SignedInUser.OrgID, namespace, srv.cfg)
|
||||
rules, err := validateRuleGroup(&ruleGroupConfig, c.SignedInUser.GetOrgID(), namespace, srv.cfg)
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusBadRequest, err, "")
|
||||
}
|
||||
|
||||
groupKey := ngmodels.AlertRuleGroupKey{
|
||||
OrgID: c.SignedInUser.OrgID,
|
||||
OrgID: c.SignedInUser.GetOrgID(),
|
||||
NamespaceUID: namespace.UID,
|
||||
RuleGroup: ruleGroupConfig.Name,
|
||||
}
|
||||
@@ -266,7 +270,9 @@ func (srv RulerSrv) updateAlertRulesInGroup(c *contextmodel.ReqContext, groupKey
|
||||
var finalChanges *store.GroupDelta
|
||||
hasAccess := accesscontrol.HasAccess(srv.ac, c)
|
||||
err := srv.xactManager.InTransaction(c.Req.Context(), func(tranCtx context.Context) error {
|
||||
logger := srv.log.New("namespace_uid", groupKey.NamespaceUID, "group", groupKey.RuleGroup, "org_id", groupKey.OrgID, "user_id", c.UserID)
|
||||
userNamespace, id := c.SignedInUser.GetNamespacedID()
|
||||
logger := srv.log.New("namespace_uid", groupKey.NamespaceUID, "group",
|
||||
groupKey.RuleGroup, "org_id", groupKey.OrgID, "user_id", id, "userNamespace", userNamespace)
|
||||
groupChanges, err := store.CalculateChanges(tranCtx, srv.store, groupKey, rules)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -303,7 +309,7 @@ func (srv RulerSrv) updateAlertRulesInGroup(c *contextmodel.ReqContext, groupKey
|
||||
UIDs = append(UIDs, rule.UID)
|
||||
}
|
||||
|
||||
if err = srv.store.DeleteAlertRulesByUID(tranCtx, c.SignedInUser.OrgID, UIDs...); err != nil {
|
||||
if err = srv.store.DeleteAlertRulesByUID(tranCtx, c.SignedInUser.GetOrgID(), UIDs...); err != nil {
|
||||
return fmt.Errorf("failed to delete rules: %w", err)
|
||||
}
|
||||
}
|
||||
@@ -343,9 +349,10 @@ func (srv RulerSrv) updateAlertRulesInGroup(c *contextmodel.ReqContext, groupKey
|
||||
}
|
||||
|
||||
if len(finalChanges.New) > 0 {
|
||||
userID, _ := identity.UserIdentifier(c.SignedInUser.GetNamespacedID())
|
||||
limitReached, err := srv.QuotaService.CheckQuotaReached(tranCtx, ngmodels.QuotaTargetSrv, "a.ScopeParameters{
|
||||
OrgID: c.SignedInUser.GetOrgID(),
|
||||
UserID: c.UserID,
|
||||
UserID: userID,
|
||||
}) // alert rule is table name
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get alert rules quota: %w", err)
|
||||
@@ -481,7 +488,7 @@ func verifyProvisionedRulesNotAffected(ctx context.Context, provenanceStore prov
|
||||
return fmt.Errorf("%w: alert rule group [%s]", errProvisionedResource, errorMsg.String())
|
||||
}
|
||||
|
||||
func validateQueries(ctx context.Context, groupChanges *store.GroupDelta, validator ConditionValidator, user *user.SignedInUser) error {
|
||||
func validateQueries(ctx context.Context, groupChanges *store.GroupDelta, validator ConditionValidator, user identity.Requester) error {
|
||||
if len(groupChanges.New) > 0 {
|
||||
for _, rule := range groupChanges.New {
|
||||
err := validator.Validate(eval.NewContext(ctx, user), rule.GetEvalCondition())
|
||||
|
||||
@@ -14,12 +14,12 @@ import (
|
||||
// ExportFromPayload converts the rule groups from the argument `ruleGroupConfig` to export format. All rules are expected to be fully specified. The access to data sources mentioned in the rules is not enforced.
|
||||
// Can return 403 StatusForbidden if user is not authorized to read folder `namespaceTitle`
|
||||
func (srv RulerSrv) ExportFromPayload(c *contextmodel.ReqContext, ruleGroupConfig apimodels.PostableRuleGroupConfig, namespaceTitle string) response.Response {
|
||||
namespace, err := srv.store.GetNamespaceByTitle(c.Req.Context(), namespaceTitle, c.SignedInUser.OrgID, c.SignedInUser)
|
||||
namespace, err := srv.store.GetNamespaceByTitle(c.Req.Context(), namespaceTitle, c.SignedInUser.GetOrgID(), c.SignedInUser)
|
||||
if err != nil {
|
||||
return toNamespaceErrorResponse(err)
|
||||
}
|
||||
|
||||
rulesWithOptionals, err := validateRuleGroup(&ruleGroupConfig, c.SignedInUser.OrgID, namespace, srv.cfg)
|
||||
rulesWithOptionals, err := validateRuleGroup(&ruleGroupConfig, c.SignedInUser.GetOrgID(), namespace, srv.cfg)
|
||||
if err != nil {
|
||||
return ErrResp(http.StatusBadRequest, err, "")
|
||||
}
|
||||
@@ -106,7 +106,7 @@ func (srv RulerSrv) getRuleWithFolderTitleByRuleUid(c *contextmodel.ReqContext,
|
||||
if err != nil {
|
||||
return ngmodels.AlertRuleGroupWithFolderTitle{}, err
|
||||
}
|
||||
namespace, err := srv.store.GetNamespaceByUID(c.Req.Context(), rule.NamespaceUID, c.SignedInUser.OrgID, c.SignedInUser)
|
||||
namespace, err := srv.store.GetNamespaceByUID(c.Req.Context(), rule.NamespaceUID, c.SignedInUser.GetOrgID(), c.SignedInUser)
|
||||
if err != nil {
|
||||
return ngmodels.AlertRuleGroupWithFolderTitle{}, errors.Join(errFolderAccess, err)
|
||||
}
|
||||
@@ -115,7 +115,7 @@ func (srv RulerSrv) getRuleWithFolderTitleByRuleUid(c *contextmodel.ReqContext,
|
||||
|
||||
// getRuleGroupWithFolderTitle calls getAuthorizedRuleGroup and combines its result with folder (aka namespace) title.
|
||||
func (srv RulerSrv) getRuleGroupWithFolderTitle(c *contextmodel.ReqContext, ruleGroupKey ngmodels.AlertRuleGroupKey) (ngmodels.AlertRuleGroupWithFolderTitle, error) {
|
||||
namespace, err := srv.store.GetNamespaceByUID(c.Req.Context(), ruleGroupKey.NamespaceUID, c.SignedInUser.OrgID, c.SignedInUser)
|
||||
namespace, err := srv.store.GetNamespaceByUID(c.Req.Context(), ruleGroupKey.NamespaceUID, c.SignedInUser.GetOrgID(), c.SignedInUser)
|
||||
if err != nil {
|
||||
return ngmodels.AlertRuleGroupWithFolderTitle{}, errors.Join(errFolderAccess, err)
|
||||
}
|
||||
|
||||
@@ -3,16 +3,16 @@ package api
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
// RuleStore is the interface for persisting alert rules and instances
|
||||
type RuleStore interface {
|
||||
GetUserVisibleNamespaces(context.Context, int64, *user.SignedInUser) (map[string]*folder.Folder, error)
|
||||
GetNamespaceByTitle(context.Context, string, int64, *user.SignedInUser) (*folder.Folder, error)
|
||||
GetNamespaceByUID(ctx context.Context, uid string, orgID int64, user *user.SignedInUser) (*folder.Folder, error)
|
||||
GetUserVisibleNamespaces(context.Context, int64, identity.Requester) (map[string]*folder.Folder, error)
|
||||
GetNamespaceByTitle(context.Context, string, int64, identity.Requester) (*folder.Folder, error)
|
||||
GetNamespaceByUID(ctx context.Context, uid string, orgID int64, user identity.Requester) (*folder.Folder, error)
|
||||
GetAlertRulesGroupByRuleUID(ctx context.Context, query *ngmodels.GetAlertRulesGroupByRuleUIDQuery) ([]*ngmodels.AlertRule, error)
|
||||
ListAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) (ngmodels.RulesGroup, error)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user