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:
@@ -981,8 +981,8 @@ func (d *dashboardStore) FindDashboards(ctx context.Context, query *dashboards.F
|
|||||||
if query.OrgId != 0 {
|
if query.OrgId != 0 {
|
||||||
orgID = query.OrgId
|
orgID = query.OrgId
|
||||||
filters = append(filters, searchstore.OrgFilter{OrgId: orgID})
|
filters = append(filters, searchstore.OrgFilter{OrgId: orgID})
|
||||||
} else if query.SignedInUser.OrgID != 0 {
|
} else if query.SignedInUser.GetOrgID() != 0 {
|
||||||
orgID = query.SignedInUser.OrgID
|
orgID = query.SignedInUser.GetOrgID()
|
||||||
filters = append(filters, searchstore.OrgFilter{OrgId: orgID})
|
filters = append(filters, searchstore.OrgFilter{OrgId: orgID})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||||
"github.com/grafana/grafana/pkg/infra/slugify"
|
"github.com/grafana/grafana/pkg/infra/slugify"
|
||||||
"github.com/grafana/grafana/pkg/kinds"
|
"github.com/grafana/grafana/pkg/kinds"
|
||||||
@@ -14,9 +16,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/org"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
"github.com/grafana/grafana/pkg/services/quota"
|
"github.com/grafana/grafana/pkg/services/quota"
|
||||||
"github.com/grafana/grafana/pkg/services/search/model"
|
"github.com/grafana/grafana/pkg/services/search/model"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const RootFolderName = "General"
|
const RootFolderName = "General"
|
||||||
@@ -482,7 +482,7 @@ type GetDashboardACLInfoListQuery struct {
|
|||||||
type FindPersistedDashboardsQuery struct {
|
type FindPersistedDashboardsQuery struct {
|
||||||
Title string
|
Title string
|
||||||
OrgId int64
|
OrgId int64
|
||||||
SignedInUser *user.SignedInUser
|
SignedInUser identity.Requester
|
||||||
DashboardIds []int64
|
DashboardIds []int64
|
||||||
DashboardUIDs []string
|
DashboardUIDs []string
|
||||||
Type string
|
Type string
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ func (s *ServiceImpl) getAdminNode(c *contextmodel.ReqContext) (*navtree.NavLink
|
|||||||
configNode := &navtree.NavLink{
|
configNode := &navtree.NavLink{
|
||||||
Id: navtree.NavIDCfg,
|
Id: navtree.NavIDCfg,
|
||||||
Text: "Administration",
|
Text: "Administration",
|
||||||
SubTitle: "Organization: " + c.OrgName,
|
SubTitle: "Organization: " + c.SignedInUser.GetOrgName(),
|
||||||
Icon: "cog",
|
Icon: "cog",
|
||||||
SortWeight: navtree.WeightConfig,
|
SortWeight: navtree.WeightConfig,
|
||||||
Children: configNodes,
|
Children: configNodes,
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
"github.com/grafana/grafana/pkg/infra/kvstore"
|
"github.com/grafana/grafana/pkg/infra/kvstore"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
|
"github.com/grafana/grafana/pkg/models/roletype"
|
||||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/apikey"
|
"github.com/grafana/grafana/pkg/services/apikey"
|
||||||
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/datasources"
|
"github.com/grafana/grafana/pkg/services/datasources"
|
||||||
@@ -240,10 +242,10 @@ func (s *ServiceImpl) addHelpLinks(treeRoot *navtree.NavTreeRoot, c *contextmode
|
|||||||
func (s *ServiceImpl) getProfileNode(c *contextmodel.ReqContext) *navtree.NavLink {
|
func (s *ServiceImpl) getProfileNode(c *contextmodel.ReqContext) *navtree.NavLink {
|
||||||
// Only set login if it's different from the name
|
// Only set login if it's different from the name
|
||||||
var login string
|
var login string
|
||||||
if c.SignedInUser.Login != c.SignedInUser.NameOrFallback() {
|
if c.SignedInUser.GetLogin() != c.SignedInUser.GetDisplayName() {
|
||||||
login = c.SignedInUser.Login
|
login = c.SignedInUser.GetLogin()
|
||||||
}
|
}
|
||||||
gravatarURL := dtos.GetGravatarUrl(c.Email)
|
gravatarURL := dtos.GetGravatarUrl(c.SignedInUser.GetEmail())
|
||||||
|
|
||||||
children := []*navtree.NavLink{
|
children := []*navtree.NavLink{
|
||||||
{
|
{
|
||||||
@@ -275,7 +277,7 @@ func (s *ServiceImpl) getProfileNode(c *contextmodel.ReqContext) *navtree.NavLin
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &navtree.NavLink{
|
return &navtree.NavLink{
|
||||||
Text: c.SignedInUser.NameOrFallback(),
|
Text: c.SignedInUser.GetDisplayName(),
|
||||||
SubTitle: login,
|
SubTitle: login,
|
||||||
Id: "profile",
|
Id: "profile",
|
||||||
Img: gravatarURL,
|
Img: gravatarURL,
|
||||||
@@ -289,8 +291,9 @@ func (s *ServiceImpl) getProfileNode(c *contextmodel.ReqContext) *navtree.NavLin
|
|||||||
func (s *ServiceImpl) buildStarredItemsNavLinks(c *contextmodel.ReqContext) ([]*navtree.NavLink, error) {
|
func (s *ServiceImpl) buildStarredItemsNavLinks(c *contextmodel.ReqContext) ([]*navtree.NavLink, error) {
|
||||||
starredItemsChildNavs := []*navtree.NavLink{}
|
starredItemsChildNavs := []*navtree.NavLink{}
|
||||||
|
|
||||||
|
userID, _ := identity.UserIdentifier(c.SignedInUser.GetNamespacedID())
|
||||||
query := star.GetUserStarsQuery{
|
query := star.GetUserStarsQuery{
|
||||||
UserID: c.SignedInUser.UserID,
|
UserID: userID,
|
||||||
}
|
}
|
||||||
|
|
||||||
starredDashboardResult, err := s.starService.GetByUser(c.Req.Context(), &query)
|
starredDashboardResult, err := s.starService.GetByUser(c.Req.Context(), &query)
|
||||||
@@ -394,7 +397,7 @@ func (s *ServiceImpl) buildLegacyAlertNavLinks(c *contextmodel.ReqContext) *navt
|
|||||||
Text: "Alert rules", Id: "alert-list", Url: s.cfg.AppSubURL + "/alerting/list", Icon: "list-ul",
|
Text: "Alert rules", Id: "alert-list", Url: s.cfg.AppSubURL + "/alerting/list", Icon: "list-ul",
|
||||||
})
|
})
|
||||||
|
|
||||||
if c.HasRole(org.RoleEditor) {
|
if c.SignedInUser.HasRole(roletype.RoleEditor) {
|
||||||
alertChildNavs = append(alertChildNavs, &navtree.NavLink{
|
alertChildNavs = append(alertChildNavs, &navtree.NavLink{
|
||||||
Text: "Notification channels", Id: "channels", Url: s.cfg.AppSubURL + "/alerting/notifications",
|
Text: "Notification channels", Id: "channels", Url: s.cfg.AppSubURL + "/alerting/notifications",
|
||||||
Icon: "comment-alt-share",
|
Icon: "comment-alt-share",
|
||||||
@@ -437,7 +440,7 @@ func (s *ServiceImpl) buildAlertNavLinks(c *contextmodel.ReqContext) *navtree.Na
|
|||||||
alertChildNavs = append(alertChildNavs, &navtree.NavLink{Text: "Alert groups", SubTitle: "See grouped alerts from an Alertmanager instance", Id: "groups", Url: s.cfg.AppSubURL + "/alerting/groups", Icon: "layer-group"})
|
alertChildNavs = append(alertChildNavs, &navtree.NavLink{Text: "Alert groups", SubTitle: "See grouped alerts from an Alertmanager instance", Id: "groups", Url: s.cfg.AppSubURL + "/alerting/groups", Icon: "layer-group"})
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.OrgRole == org.RoleAdmin {
|
if c.SignedInUser.GetOrgRole() == org.RoleAdmin {
|
||||||
alertChildNavs = append(alertChildNavs, &navtree.NavLink{
|
alertChildNavs = append(alertChildNavs, &navtree.NavLink{
|
||||||
Text: "Admin", Id: "alerting-admin", Url: s.cfg.AppSubURL + "/alerting/admin",
|
Text: "Admin", Id: "alerting-admin", Url: s.cfg.AppSubURL + "/alerting/admin",
|
||||||
Icon: "cog",
|
Icon: "cog",
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ func (srv ConfigSrv) RouteGetAlertmanagers(c *contextmodel.ReqContext) response.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (srv ConfigSrv) RouteGetNGalertConfig(c *contextmodel.ReqContext) response.Response {
|
func (srv ConfigSrv) RouteGetNGalertConfig(c *contextmodel.ReqContext) response.Response {
|
||||||
if c.OrgRole != org.RoleAdmin {
|
if c.SignedInUser.GetOrgRole() != org.RoleAdmin {
|
||||||
return accessForbiddenResp()
|
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 {
|
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()
|
return accessForbiddenResp()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ func (srv ConfigSrv) RoutePostNGalertConfig(c *contextmodel.ReqContext, body api
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (srv ConfigSrv) RouteDeleteNGalertConfig(c *contextmodel.ReqContext) response.Response {
|
func (srv ConfigSrv) RouteDeleteNGalertConfig(c *contextmodel.ReqContext) response.Response {
|
||||||
if c.OrgRole != org.RoleAdmin {
|
if c.SignedInUser.GetOrgRole() != org.RoleAdmin {
|
||||||
return accessForbiddenResp()
|
return accessForbiddenResp()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ func (srv PrometheusSrv) RouteGetRuleStatuses(c *contextmodel.ReqContext) respon
|
|||||||
}
|
}
|
||||||
|
|
||||||
alertRuleQuery := ngmodels.ListAlertRulesQuery{
|
alertRuleQuery := ngmodels.ListAlertRulesQuery{
|
||||||
OrgID: c.SignedInUser.OrgID,
|
OrgID: c.SignedInUser.GetOrgID(),
|
||||||
NamespaceUIDs: namespaceUIDs,
|
NamespaceUIDs: namespaceUIDs,
|
||||||
DashboardUID: dashboardUID,
|
DashboardUID: dashboardUID,
|
||||||
PanelID: panelID,
|
PanelID: panelID,
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
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/hcl"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||||
alerting_models "github.com/grafana/grafana/pkg/services/ngalert/models"
|
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/provisioning"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ type ProvisioningSrv struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ContactPointService interface {
|
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)
|
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
|
UpdateContactPoint(ctx context.Context, orgID int64, contactPoint definitions.EmbeddedContactPoint, p alerting_models.Provenance) error
|
||||||
DeleteContactPoint(ctx context.Context, orgID int64, uid string) 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, "")
|
return ErrResp(http.StatusBadRequest, err, "")
|
||||||
}
|
}
|
||||||
provenance := determineProvenance(c)
|
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) {
|
if errors.Is(err, alerting_models.ErrAlertRuleFailedValidation) {
|
||||||
return ErrResp(http.StatusBadRequest, err, "")
|
return ErrResp(http.StatusBadRequest, err, "")
|
||||||
}
|
}
|
||||||
@@ -478,7 +479,9 @@ func (srv *ProvisioningSrv) RoutePutAlertRuleGroup(c *contextmodel.ReqContext, a
|
|||||||
ErrResp(http.StatusBadRequest, err, "")
|
ErrResp(http.StatusBadRequest, err, "")
|
||||||
}
|
}
|
||||||
provenance := determineProvenance(c)
|
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) {
|
if errors.Is(err, alerting_models.ErrAlertRuleUniqueConstraintViolation) {
|
||||||
return ErrResp(http.StatusBadRequest, err, "")
|
return ErrResp(http.StatusBadRequest, err, "")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
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/provisioning"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||||
"github.com/grafana/grafana/pkg/services/quota"
|
"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/setting"
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"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.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.
|
// 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 {
|
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 {
|
if err != nil {
|
||||||
return toNamespaceErrorResponse(err)
|
return toNamespaceErrorResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userNamespace, id := c.SignedInUser.GetNamespacedID()
|
||||||
var loggerCtx = []any{
|
var loggerCtx = []any{
|
||||||
"userId",
|
"userId",
|
||||||
c.SignedInUser.UserID,
|
id,
|
||||||
|
"userNamespace",
|
||||||
|
userNamespace,
|
||||||
"namespaceUid",
|
"namespaceUid",
|
||||||
namespace.UID,
|
namespace.UID,
|
||||||
}
|
}
|
||||||
@@ -68,7 +71,7 @@ func (srv RulerSrv) RouteDeleteAlertRules(c *contextmodel.ReqContext, namespaceT
|
|||||||
}
|
}
|
||||||
logger := srv.log.New(loggerCtx...)
|
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 {
|
if err != nil {
|
||||||
return ErrResp(http.StatusInternalServerError, err, "failed to fetch provenances of alert rules")
|
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...)
|
rulesToDelete = append(rulesToDelete, uid...)
|
||||||
}
|
}
|
||||||
if len(rulesToDelete) > 0 {
|
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 {
|
if err != nil {
|
||||||
return err
|
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
|
// RouteGetNamespaceRulesConfig returns all rules in a specific folder that user has access to
|
||||||
func (srv RulerSrv) RouteGetNamespaceRulesConfig(c *contextmodel.ReqContext, namespaceTitle string) response.Response {
|
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 {
|
if err != nil {
|
||||||
return toNamespaceErrorResponse(err)
|
return toNamespaceErrorResponse(err)
|
||||||
}
|
}
|
||||||
@@ -150,7 +153,7 @@ func (srv RulerSrv) RouteGetNamespaceRulesConfig(c *contextmodel.ReqContext, nam
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errorToResponse(err)
|
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 {
|
if err != nil {
|
||||||
return ErrResp(http.StatusInternalServerError, err, "failed to get provenance for rule group")
|
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).
|
// 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
|
// 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 {
|
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 {
|
if err != nil {
|
||||||
return toNamespaceErrorResponse(err)
|
return toNamespaceErrorResponse(err)
|
||||||
}
|
}
|
||||||
@@ -181,7 +184,7 @@ func (srv RulerSrv) RouteGetRulesGroupConfig(c *contextmodel.ReqContext, namespa
|
|||||||
return errorToResponse(err)
|
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 {
|
if err != nil {
|
||||||
return ErrResp(http.StatusInternalServerError, err, "failed to get group alert rules")
|
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 {
|
if err != nil {
|
||||||
return errorToResponse(err)
|
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 {
|
if err != nil {
|
||||||
return ErrResp(http.StatusInternalServerError, err, "failed to get alert rules")
|
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 {
|
for groupKey, rules := range configs {
|
||||||
folder, ok := namespaceMap[groupKey.NamespaceUID]
|
folder, ok := namespaceMap[groupKey.NamespaceUID]
|
||||||
if !ok {
|
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
|
continue
|
||||||
}
|
}
|
||||||
namespace := folder.Title
|
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 {
|
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 {
|
if err != nil {
|
||||||
return toNamespaceErrorResponse(err)
|
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 {
|
if err != nil {
|
||||||
return ErrResp(http.StatusBadRequest, err, "")
|
return ErrResp(http.StatusBadRequest, err, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
groupKey := ngmodels.AlertRuleGroupKey{
|
groupKey := ngmodels.AlertRuleGroupKey{
|
||||||
OrgID: c.SignedInUser.OrgID,
|
OrgID: c.SignedInUser.GetOrgID(),
|
||||||
NamespaceUID: namespace.UID,
|
NamespaceUID: namespace.UID,
|
||||||
RuleGroup: ruleGroupConfig.Name,
|
RuleGroup: ruleGroupConfig.Name,
|
||||||
}
|
}
|
||||||
@@ -266,7 +270,9 @@ func (srv RulerSrv) updateAlertRulesInGroup(c *contextmodel.ReqContext, groupKey
|
|||||||
var finalChanges *store.GroupDelta
|
var finalChanges *store.GroupDelta
|
||||||
hasAccess := accesscontrol.HasAccess(srv.ac, c)
|
hasAccess := accesscontrol.HasAccess(srv.ac, c)
|
||||||
err := srv.xactManager.InTransaction(c.Req.Context(), func(tranCtx context.Context) error {
|
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)
|
groupChanges, err := store.CalculateChanges(tranCtx, srv.store, groupKey, rules)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -303,7 +309,7 @@ func (srv RulerSrv) updateAlertRulesInGroup(c *contextmodel.ReqContext, groupKey
|
|||||||
UIDs = append(UIDs, rule.UID)
|
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)
|
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 {
|
if len(finalChanges.New) > 0 {
|
||||||
|
userID, _ := identity.UserIdentifier(c.SignedInUser.GetNamespacedID())
|
||||||
limitReached, err := srv.QuotaService.CheckQuotaReached(tranCtx, ngmodels.QuotaTargetSrv, "a.ScopeParameters{
|
limitReached, err := srv.QuotaService.CheckQuotaReached(tranCtx, ngmodels.QuotaTargetSrv, "a.ScopeParameters{
|
||||||
OrgID: c.SignedInUser.GetOrgID(),
|
OrgID: c.SignedInUser.GetOrgID(),
|
||||||
UserID: c.UserID,
|
UserID: userID,
|
||||||
}) // alert rule is table name
|
}) // alert rule is table name
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get alert rules quota: %w", err)
|
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())
|
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 {
|
if len(groupChanges.New) > 0 {
|
||||||
for _, rule := range groupChanges.New {
|
for _, rule := range groupChanges.New {
|
||||||
err := validator.Validate(eval.NewContext(ctx, user), rule.GetEvalCondition())
|
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.
|
// 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`
|
// 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 {
|
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 {
|
if err != nil {
|
||||||
return toNamespaceErrorResponse(err)
|
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 {
|
if err != nil {
|
||||||
return ErrResp(http.StatusBadRequest, err, "")
|
return ErrResp(http.StatusBadRequest, err, "")
|
||||||
}
|
}
|
||||||
@@ -106,7 +106,7 @@ func (srv RulerSrv) getRuleWithFolderTitleByRuleUid(c *contextmodel.ReqContext,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return ngmodels.AlertRuleGroupWithFolderTitle{}, err
|
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 {
|
if err != nil {
|
||||||
return ngmodels.AlertRuleGroupWithFolderTitle{}, errors.Join(errFolderAccess, err)
|
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.
|
// 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) {
|
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 {
|
if err != nil {
|
||||||
return ngmodels.AlertRuleGroupWithFolderTitle{}, errors.Join(errFolderAccess, err)
|
return ngmodels.AlertRuleGroupWithFolderTitle{}, errors.Join(errFolderAccess, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ package api
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
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
|
// RuleStore is the interface for persisting alert rules and instances
|
||||||
type RuleStore interface {
|
type RuleStore interface {
|
||||||
GetUserVisibleNamespaces(context.Context, int64, *user.SignedInUser) (map[string]*folder.Folder, error)
|
GetUserVisibleNamespaces(context.Context, int64, identity.Requester) (map[string]*folder.Folder, error)
|
||||||
GetNamespaceByTitle(context.Context, string, int64, *user.SignedInUser) (*folder.Folder, error)
|
GetNamespaceByTitle(context.Context, string, int64, identity.Requester) (*folder.Folder, error)
|
||||||
GetNamespaceByUID(ctx context.Context, uid string, orgID int64, user *user.SignedInUser) (*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)
|
GetAlertRulesGroupByRuleUID(ctx context.Context, query *ngmodels.GetAlertRulesGroupByRuleUIDQuery) ([]*ngmodels.AlertRule, error)
|
||||||
ListAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) (ngmodels.RulesGroup, error)
|
ListAlertRules(ctx context.Context, query *ngmodels.ListAlertRulesQuery) (ngmodels.RulesGroup, error)
|
||||||
|
|
||||||
|
|||||||
@@ -9,14 +9,15 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/benbjohnson/clock"
|
"github.com/benbjohnson/clock"
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/state"
|
"github.com/grafana/grafana/pkg/services/ngalert/state"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -61,7 +62,7 @@ func NewEngine(appUrl *url.URL, evalFactory eval.EvaluatorFactory, tracer tracin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Engine) Test(ctx context.Context, user *user.SignedInUser, rule *models.AlertRule, from, to time.Time) (*data.Frame, error) {
|
func (e *Engine) Test(ctx context.Context, user identity.Requester, rule *models.AlertRule, from, to time.Time) (*data.Frame, error) {
|
||||||
ruleCtx := models.WithRuleKey(ctx, rule.GetKey())
|
ruleCtx := models.WithRuleKey(ctx, rule.GetKey())
|
||||||
logger := logger.FromContext(ctx)
|
logger := logger.FromContext(ctx)
|
||||||
|
|
||||||
@@ -125,7 +126,7 @@ func (e *Engine) Test(ctx context.Context, user *user.SignedInUser, rule *models
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBacktestingEvaluator(ctx context.Context, evalFactory eval.EvaluatorFactory, user *user.SignedInUser, condition models.Condition) (backtestingEvaluator, error) {
|
func newBacktestingEvaluator(ctx context.Context, evalFactory eval.EvaluatorFactory, user identity.Requester, condition models.Condition) (backtestingEvaluator, error) {
|
||||||
for _, q := range condition.Data {
|
for _, q := range condition.Data {
|
||||||
if q.DatasourceUID == "__data__" || q.QueryType == "__data__" {
|
if q.DatasourceUID == "__data__" || q.QueryType == "__data__" {
|
||||||
if len(condition.Data) != 1 {
|
if len(condition.Data) != 1 {
|
||||||
|
|||||||
@@ -9,14 +9,15 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/eval/eval_mocks"
|
"github.com/grafana/grafana/pkg/services/ngalert/eval/eval_mocks"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/state"
|
"github.com/grafana/grafana/pkg/services/ngalert/state"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -174,7 +175,7 @@ func TestEvaluatorTest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
manager := &fakeStateManager{}
|
manager := &fakeStateManager{}
|
||||||
|
|
||||||
backtestingEvaluatorFactory = func(ctx context.Context, evalFactory eval.EvaluatorFactory, user *user.SignedInUser, condition models.Condition) (backtestingEvaluator, error) {
|
backtestingEvaluatorFactory = func(ctx context.Context, evalFactory eval.EvaluatorFactory, user identity.Requester, condition models.Condition) (backtestingEvaluator, error) {
|
||||||
return evaluator, nil
|
return evaluator, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ package eval
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EvaluationContext represents the context in which a condition is evaluated.
|
// EvaluationContext represents the context in which a condition is evaluated.
|
||||||
type EvaluationContext struct {
|
type EvaluationContext struct {
|
||||||
Ctx context.Context
|
Ctx context.Context
|
||||||
User *user.SignedInUser
|
User identity.Requester
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContext(ctx context.Context, user *user.SignedInUser) EvaluationContext {
|
func NewContext(ctx context.Context, user identity.Requester) EvaluationContext {
|
||||||
return EvaluationContext{
|
return EvaluationContext{
|
||||||
Ctx: ctx,
|
Ctx: ctx,
|
||||||
User: user,
|
User: user,
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ func buildDatasourceHeaders(ctx context.Context) map[string]string {
|
|||||||
// getExprRequest validates the condition, gets the datasource information and creates an expr.Request from it.
|
// getExprRequest validates the condition, gets the datasource information and creates an expr.Request from it.
|
||||||
func getExprRequest(ctx EvaluationContext, data []models.AlertQuery, dsCacheService datasources.CacheService) (*expr.Request, error) {
|
func getExprRequest(ctx EvaluationContext, data []models.AlertQuery, dsCacheService datasources.CacheService) (*expr.Request, error) {
|
||||||
req := &expr.Request{
|
req := &expr.Request{
|
||||||
OrgId: ctx.User.OrgID,
|
OrgId: ctx.User.GetOrgID(),
|
||||||
Headers: buildDatasourceHeaders(ctx.Ctx),
|
Headers: buildDatasourceHeaders(ctx.Ctx),
|
||||||
User: ctx.User,
|
User: ctx.User,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package models
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HistoryQuery represents a query for alert state history.
|
// HistoryQuery represents a query for alert state history.
|
||||||
@@ -16,5 +16,5 @@ type HistoryQuery struct {
|
|||||||
From time.Time
|
From time.Time
|
||||||
To time.Time
|
To time.Time
|
||||||
Limit int
|
Limit int
|
||||||
SignedInUser *user.SignedInUser
|
SignedInUser identity.Requester
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/log"
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
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/models"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels_config"
|
"github.com/grafana/grafana/pkg/services/ngalert/notifier/channels_config"
|
||||||
"github.com/grafana/grafana/pkg/services/secrets"
|
"github.com/grafana/grafana/pkg/services/secrets"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ type ContactPointQuery struct {
|
|||||||
Decrypt bool
|
Decrypt bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ecp *ContactPointService) canDecryptSecrets(ctx context.Context, u *user.SignedInUser) bool {
|
func (ecp *ContactPointService) canDecryptSecrets(ctx context.Context, u identity.Requester) bool {
|
||||||
if u == nil {
|
if u == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -64,7 +64,7 @@ func (ecp *ContactPointService) canDecryptSecrets(ctx context.Context, u *user.S
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetContactPoints returns contact points. If q.Decrypt is true and the user is an OrgAdmin, decrypted secure settings are included instead of redacted ones.
|
// GetContactPoints returns contact points. If q.Decrypt is true and the user is an OrgAdmin, decrypted secure settings are included instead of redacted ones.
|
||||||
func (ecp *ContactPointService) GetContactPoints(ctx context.Context, q ContactPointQuery, u *user.SignedInUser) ([]apimodels.EmbeddedContactPoint, error) {
|
func (ecp *ContactPointService) GetContactPoints(ctx context.Context, q ContactPointQuery, u identity.Requester) ([]apimodels.EmbeddedContactPoint, error) {
|
||||||
if q.Decrypt && !ecp.canDecryptSecrets(ctx, u) {
|
if q.Decrypt && !ecp.canDecryptSecrets(ctx, u) {
|
||||||
return nil, fmt.Errorf("%w: user requires Admin role or alert.provisioning.secrets:read permission to view decrypted secure settings", ErrPermissionDenied)
|
return nil, fmt.Errorf("%w: user requires Admin role or alert.provisioning.secrets:read permission to view decrypted secure settings", ErrPermissionDenied)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||||
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
|
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
|
||||||
"github.com/grafana/grafana/pkg/services/store/entity"
|
"github.com/grafana/grafana/pkg/services/store/entity"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -426,7 +425,7 @@ func (st DBstore) GetRuleGroupInterval(ctx context.Context, orgID int64, namespa
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetUserVisibleNamespaces returns the folders that are visible to the user and have at least one alert in it
|
// GetUserVisibleNamespaces returns the folders that are visible to the user and have at least one alert in it
|
||||||
func (st DBstore) GetUserVisibleNamespaces(ctx context.Context, orgID int64, user *user.SignedInUser) (map[string]*folder.Folder, error) {
|
func (st DBstore) GetUserVisibleNamespaces(ctx context.Context, orgID int64, user identity.Requester) (map[string]*folder.Folder, error) {
|
||||||
namespaceMap := make(map[string]*folder.Folder)
|
namespaceMap := make(map[string]*folder.Folder)
|
||||||
|
|
||||||
searchQuery := dashboards.FindPersistedDashboardsQuery{
|
searchQuery := dashboards.FindPersistedDashboardsQuery{
|
||||||
@@ -470,7 +469,7 @@ func (st DBstore) GetUserVisibleNamespaces(ctx context.Context, orgID int64, use
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetNamespaceByTitle is a handler for retrieving a namespace by its title. Alerting rules follow a Grafana folder-like structure which we call namespaces.
|
// GetNamespaceByTitle is a handler for retrieving a namespace by its title. Alerting rules follow a Grafana folder-like structure which we call namespaces.
|
||||||
func (st DBstore) GetNamespaceByTitle(ctx context.Context, namespace string, orgID int64, user *user.SignedInUser) (*folder.Folder, error) {
|
func (st DBstore) GetNamespaceByTitle(ctx context.Context, namespace string, orgID int64, user identity.Requester) (*folder.Folder, error) {
|
||||||
folder, err := st.FolderService.Get(ctx, &folder.GetFolderQuery{OrgID: orgID, Title: &namespace, SignedInUser: user})
|
folder, err := st.FolderService.Get(ctx, &folder.GetFolderQuery{OrgID: orgID, Title: &namespace, SignedInUser: user})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -480,7 +479,7 @@ func (st DBstore) GetNamespaceByTitle(ctx context.Context, namespace string, org
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetNamespaceByUID is a handler for retrieving a namespace by its UID. Alerting rules follow a Grafana folder-like structure which we call namespaces.
|
// GetNamespaceByUID is a handler for retrieving a namespace by its UID. Alerting rules follow a Grafana folder-like structure which we call namespaces.
|
||||||
func (st DBstore) GetNamespaceByUID(ctx context.Context, uid string, orgID int64, user *user.SignedInUser) (*folder.Folder, error) {
|
func (st DBstore) GetNamespaceByUID(ctx context.Context, uid string, orgID int64, user identity.Requester) (*folder.Folder, error) {
|
||||||
folder, err := st.FolderService.Get(ctx, &folder.GetFolderQuery{OrgID: orgID, UID: &uid, SignedInUser: user})
|
folder, err := st.FolderService.Get(ctx, &folder.GetFolderQuery{OrgID: orgID, UID: &uid, SignedInUser: user})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
|
||||||
"github.com/grafana/grafana/pkg/util"
|
"github.com/grafana/grafana/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -229,7 +228,7 @@ func (f *RuleStore) ListAlertRules(_ context.Context, q *models.ListAlertRulesQu
|
|||||||
return ruleList, nil
|
return ruleList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RuleStore) GetUserVisibleNamespaces(_ context.Context, orgID int64, _ *user.SignedInUser) (map[string]*folder.Folder, error) {
|
func (f *RuleStore) GetUserVisibleNamespaces(_ context.Context, orgID int64, _ identity.Requester) (map[string]*folder.Folder, error) {
|
||||||
f.mtx.Lock()
|
f.mtx.Lock()
|
||||||
defer f.mtx.Unlock()
|
defer f.mtx.Unlock()
|
||||||
|
|
||||||
@@ -246,7 +245,7 @@ func (f *RuleStore) GetUserVisibleNamespaces(_ context.Context, orgID int64, _ *
|
|||||||
return namespacesMap, nil
|
return namespacesMap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RuleStore) GetNamespaceByTitle(_ context.Context, title string, orgID int64, _ *user.SignedInUser) (*folder.Folder, error) {
|
func (f *RuleStore) GetNamespaceByTitle(_ context.Context, title string, orgID int64, _ identity.Requester) (*folder.Folder, error) {
|
||||||
folders := f.Folders[orgID]
|
folders := f.Folders[orgID]
|
||||||
for _, folder := range folders {
|
for _, folder := range folders {
|
||||||
if folder.Title == title {
|
if folder.Title == title {
|
||||||
@@ -256,7 +255,7 @@ func (f *RuleStore) GetNamespaceByTitle(_ context.Context, title string, orgID i
|
|||||||
return nil, fmt.Errorf("not found")
|
return nil, fmt.Errorf("not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *RuleStore) GetNamespaceByUID(_ context.Context, uid string, orgID int64, _ *user.SignedInUser) (*folder.Folder, error) {
|
func (f *RuleStore) GetNamespaceByUID(_ context.Context, uid string, orgID int64, _ identity.Requester) (*folder.Folder, error) {
|
||||||
f.RecordedOps = append(f.RecordedOps, GenericRecordedQuery{
|
f.RecordedOps = append(f.RecordedOps, GenericRecordedQuery{
|
||||||
Name: "GetNamespaceByUID",
|
Name: "GetNamespaceByUID",
|
||||||
Params: []any{orgID, uid},
|
Params: []any{orgID, uid},
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||||
"github.com/grafana/grafana/pkg/plugins"
|
"github.com/grafana/grafana/pkg/plugins"
|
||||||
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
"github.com/grafana/grafana/pkg/services/contexthandler"
|
"github.com/grafana/grafana/pkg/services/contexthandler"
|
||||||
"github.com/grafana/grafana/pkg/util/proxyutil"
|
"github.com/grafana/grafana/pkg/util/proxyutil"
|
||||||
)
|
)
|
||||||
@@ -31,8 +32,9 @@ func (m *UserHeaderMiddleware) applyUserHeader(ctx context.Context, h backend.Fo
|
|||||||
}
|
}
|
||||||
|
|
||||||
h.DeleteHTTPHeader(proxyutil.UserHeaderName)
|
h.DeleteHTTPHeader(proxyutil.UserHeaderName)
|
||||||
if !reqCtx.IsAnonymous {
|
namespace, _ := reqCtx.SignedInUser.GetNamespacedID()
|
||||||
h.SetHTTPHeader(proxyutil.UserHeaderName, reqCtx.Login)
|
if namespace != identity.NamespaceAnonymous {
|
||||||
|
h.SetHTTPHeader(proxyutil.UserHeaderName, reqCtx.SignedInUser.GetLogin())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user