mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
* Alerting: Add single rule checks to alert rule access control Modifies ruler api single rule read to no longer fetch entire groups and instead use the new single rule ac check. Simplifies provisioning api getAlertRuleAuthorized logic to always load a single rule instead of conditionally loading the entire group when provisioning permissions are not present. * Swap out Has/AuthorizeAccessToRule for Has/AuthorizeAccessInFolder
165 lines
4.8 KiB
Go
165 lines
4.8 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
|
|
|
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/state"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
)
|
|
|
|
type fakeAlertInstanceManager struct {
|
|
mtx sync.Mutex
|
|
// orgID -> RuleID -> States
|
|
states map[int64]map[string][]*state.State
|
|
}
|
|
|
|
func NewFakeAlertInstanceManager(t *testing.T) *fakeAlertInstanceManager {
|
|
t.Helper()
|
|
|
|
return &fakeAlertInstanceManager{
|
|
states: map[int64]map[string][]*state.State{},
|
|
}
|
|
}
|
|
|
|
func (f *fakeAlertInstanceManager) GetAll(orgID int64) []*state.State {
|
|
f.mtx.Lock()
|
|
defer f.mtx.Unlock()
|
|
var s []*state.State
|
|
|
|
for orgID := range f.states {
|
|
for _, states := range f.states[orgID] {
|
|
s = append(s, states...)
|
|
}
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
func (f *fakeAlertInstanceManager) GetStatesForRuleUID(orgID int64, alertRuleUID string) []*state.State {
|
|
f.mtx.Lock()
|
|
defer f.mtx.Unlock()
|
|
return f.states[orgID][alertRuleUID]
|
|
}
|
|
|
|
// forEachState represents the callback used when generating alert instances that allows us to modify the generated result
|
|
type forEachState func(s *state.State) *state.State
|
|
|
|
func (f *fakeAlertInstanceManager) GenerateAlertInstances(orgID int64, alertRuleUID string, count int, callbacks ...forEachState) {
|
|
f.mtx.Lock()
|
|
defer f.mtx.Unlock()
|
|
|
|
evaluationTime := timeNow()
|
|
evaluationDuration := 1 * time.Minute
|
|
|
|
for i := 0; i < count; i++ {
|
|
_, ok := f.states[orgID]
|
|
if !ok {
|
|
f.states[orgID] = map[string][]*state.State{}
|
|
}
|
|
_, ok = f.states[orgID][alertRuleUID]
|
|
if !ok {
|
|
f.states[orgID][alertRuleUID] = []*state.State{}
|
|
}
|
|
|
|
newState := &state.State{
|
|
AlertRuleUID: alertRuleUID,
|
|
OrgID: 1,
|
|
Labels: data.Labels{
|
|
"__alert_rule_namespace_uid__": "test_namespace_uid",
|
|
"__alert_rule_uid__": fmt.Sprintf("test_alert_rule_uid_%v", i),
|
|
"alertname": fmt.Sprintf("test_title_%v", i),
|
|
"label": "test",
|
|
"instance_label": "test",
|
|
},
|
|
State: eval.Normal,
|
|
LatestResult: &state.Evaluation{
|
|
EvaluationTime: evaluationTime.Add(1 * time.Minute),
|
|
EvaluationState: eval.Normal,
|
|
Values: make(map[string]*float64),
|
|
},
|
|
LastEvaluationTime: evaluationTime.Add(1 * time.Minute),
|
|
EvaluationDuration: evaluationDuration,
|
|
Annotations: map[string]string{"annotation": "test"},
|
|
}
|
|
|
|
if len(callbacks) != 0 {
|
|
for _, cb := range callbacks {
|
|
newState = cb(newState)
|
|
}
|
|
}
|
|
|
|
f.states[orgID][alertRuleUID] = append(f.states[orgID][alertRuleUID], newState)
|
|
}
|
|
}
|
|
|
|
type recordingAccessControlFake struct {
|
|
Disabled bool
|
|
EvaluateRecordings []struct {
|
|
User *user.SignedInUser
|
|
Evaluator ac.Evaluator
|
|
}
|
|
Callback func(user *user.SignedInUser, evaluator ac.Evaluator) (bool, error)
|
|
}
|
|
|
|
func (a *recordingAccessControlFake) Evaluate(ctx context.Context, ur identity.Requester, evaluator ac.Evaluator) (bool, error) {
|
|
u := ur.(*user.SignedInUser)
|
|
a.EvaluateRecordings = append(a.EvaluateRecordings, struct {
|
|
User *user.SignedInUser
|
|
Evaluator ac.Evaluator
|
|
}{User: u, Evaluator: evaluator})
|
|
if a.Callback == nil {
|
|
return false, nil
|
|
}
|
|
return a.Callback(u, evaluator)
|
|
}
|
|
|
|
func (a *recordingAccessControlFake) RegisterScopeAttributeResolver(prefix string, resolver ac.ScopeAttributeResolver) {
|
|
// TODO implement me
|
|
panic("implement me")
|
|
}
|
|
|
|
func (a *recordingAccessControlFake) IsDisabled() bool {
|
|
return a.Disabled
|
|
}
|
|
|
|
var _ ac.AccessControl = &recordingAccessControlFake{}
|
|
|
|
type fakeRuleAccessControlService struct {
|
|
}
|
|
|
|
func (f fakeRuleAccessControlService) HasAccessToRuleGroup(ctx context.Context, user identity.Requester, rules models.RulesGroup) (bool, error) {
|
|
return true, nil
|
|
}
|
|
|
|
func (f fakeRuleAccessControlService) AuthorizeAccessToRuleGroup(ctx context.Context, user identity.Requester, rules models.RulesGroup) error {
|
|
return nil
|
|
}
|
|
|
|
func (f fakeRuleAccessControlService) AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, namespaced accesscontrol.Namespaced) error {
|
|
return nil
|
|
}
|
|
|
|
func (f fakeRuleAccessControlService) AuthorizeRuleChanges(ctx context.Context, user identity.Requester, change *store.GroupDelta) error {
|
|
return nil
|
|
}
|
|
|
|
func (f fakeRuleAccessControlService) AuthorizeDatasourceAccessForRule(ctx context.Context, user identity.Requester, rule *models.AlertRule) error {
|
|
return nil
|
|
}
|
|
|
|
func (f fakeRuleAccessControlService) AuthorizeDatasourceAccessForRuleGroup(ctx context.Context, user identity.Requester, rules models.RulesGroup) error {
|
|
return nil
|
|
}
|