Alerting: Move alertmanager api silence code to separate files (#86947)
* Move alertmanager api silence code to separate files unchanged * Replace with silence model instead interface --------- Co-authored-by: Matt Jacobson <matthew.jacobson@grafana.com>
This commit is contained in:
co-authored by
Matt Jacobson
parent
0f98bd3b7b
commit
dff7cb9afb
@@ -9,6 +9,7 @@ import (
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -78,10 +79,6 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
type Silence interface {
|
||||
GetRuleUID() *string
|
||||
}
|
||||
|
||||
type RuleUIDToNamespaceStore interface {
|
||||
GetNamespacesByRuleUID(ctx context.Context, orgID int64, uids ...string) (map[string]string, error)
|
||||
}
|
||||
@@ -104,7 +101,7 @@ func NewSilenceService(ac ac.AccessControl, store RuleUIDToNamespaceStore) *Sile
|
||||
// Global silence (one that is not attached to a particular rule) is considered available to all users.
|
||||
// For silences that are not attached to a rule, are checked against authorization.
|
||||
// This method is more preferred when many silences need to be checked.
|
||||
func (s SilenceService) FilterByAccess(ctx context.Context, user identity.Requester, silences ...Silence) ([]Silence, error) {
|
||||
func (s SilenceService) FilterByAccess(ctx context.Context, user identity.Requester, silences ...*models.Silence) ([]*models.Silence, error) {
|
||||
canAll, err := s.HasAccess(ctx, user, readAllSilencesEvaluator)
|
||||
if err != nil || canAll { // return early if user can either read all silences or there is an error
|
||||
return silences, err
|
||||
@@ -113,8 +110,8 @@ func (s SilenceService) FilterByAccess(ctx context.Context, user identity.Reques
|
||||
if err != nil || !canSome {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]Silence, 0, len(silences))
|
||||
silencesByRuleUID := make(map[string][]Silence, len(silences))
|
||||
result := make([]*models.Silence, 0, len(silences))
|
||||
silencesByRuleUID := make(map[string][]*models.Silence, len(silences))
|
||||
for _, silence := range silences {
|
||||
ruleUID := silence.GetRuleUID()
|
||||
if ruleUID == nil { // if this is a general silence
|
||||
@@ -154,7 +151,7 @@ func (s SilenceService) FilterByAccess(ctx context.Context, user identity.Reques
|
||||
}
|
||||
|
||||
// AuthorizeReadSilence checks if user has access to read a silence
|
||||
func (s SilenceService) AuthorizeReadSilence(ctx context.Context, user identity.Requester, silence Silence) error {
|
||||
func (s SilenceService) AuthorizeReadSilence(ctx context.Context, user identity.Requester, silence *models.Silence) error {
|
||||
canAll, err := s.HasAccess(ctx, user, readAllSilencesEvaluator)
|
||||
if canAll || err != nil { // return early if user can either read all silences or there is error
|
||||
return err
|
||||
@@ -186,7 +183,7 @@ func (s SilenceService) AuthorizeReadSilence(ctx context.Context, user identity.
|
||||
}
|
||||
|
||||
// AuthorizeCreateSilence checks if user has access to create a silence. Returns ErrAuthorizationBase if user is not authorized
|
||||
func (s SilenceService) AuthorizeCreateSilence(ctx context.Context, user identity.Requester, silence Silence) error {
|
||||
func (s SilenceService) AuthorizeCreateSilence(ctx context.Context, user identity.Requester, silence *models.Silence) error {
|
||||
canAny, err := s.HasAccess(ctx, user, createAnySilenceEvaluator)
|
||||
if err != nil || canAny {
|
||||
// return early if user can either create any silence or there is an error
|
||||
@@ -215,7 +212,7 @@ func (s SilenceService) AuthorizeCreateSilence(ctx context.Context, user identit
|
||||
}
|
||||
|
||||
// AuthorizeUpdateSilence checks if user has access to update\expire a silence. Returns ErrAuthorizationBase if user is not authorized
|
||||
func (s SilenceService) AuthorizeUpdateSilence(ctx context.Context, user identity.Requester, silence Silence) error {
|
||||
func (s SilenceService) AuthorizeUpdateSilence(ctx context.Context, user identity.Requester, silence *models.Silence) error {
|
||||
canAny, err := s.HasAccess(ctx, user, updateAnySilenceEvaluator)
|
||||
if err != nil || canAny {
|
||||
// return early if user can either update any silence or there is an error
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils"
|
||||
)
|
||||
@@ -18,15 +19,15 @@ import (
|
||||
var orgID = rand.Int63()
|
||||
|
||||
func TestFilterByAccess(t *testing.T) {
|
||||
global := testSilence{ID: "global", RuleUID: nil}
|
||||
ruleSilence1 := testSilence{ID: "rule-1", RuleUID: utils.Pointer("rule-1-uid")}
|
||||
global := testSilence("global", nil)
|
||||
ruleSilence1 := testSilence("rule-1", utils.Pointer("rule-1-uid"))
|
||||
folder1 := "rule-1-folder-uid"
|
||||
folder1Scope := dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder1)
|
||||
ruleSilence2 := testSilence{ID: "rule-2", RuleUID: utils.Pointer("rule-2-uid")}
|
||||
ruleSilence2 := testSilence("rule-2", utils.Pointer("rule-2-uid"))
|
||||
folder2 := "rule-2-folder-uid"
|
||||
notFoundRule := testSilence{ID: "unknown-rule", RuleUID: utils.Pointer("unknown-rule-uid")}
|
||||
notFoundRule := testSilence("unknown-rule", utils.Pointer("unknown-rule-uid"))
|
||||
|
||||
silences := []Silence{
|
||||
silences := []*models.Silence{
|
||||
global,
|
||||
ruleSilence1,
|
||||
ruleSilence2,
|
||||
@@ -36,19 +37,19 @@ func TestFilterByAccess(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
user identity.Requester
|
||||
expected []Silence
|
||||
expected []*models.Silence
|
||||
expectedDbAccess bool
|
||||
}{
|
||||
{
|
||||
name: "no silence access, empty list",
|
||||
user: newUser(),
|
||||
expected: []Silence{},
|
||||
expected: []*models.Silence{},
|
||||
expectedDbAccess: false,
|
||||
},
|
||||
{
|
||||
name: "instance reader should get all",
|
||||
user: newUser(ac.Permission{Action: instancesRead}),
|
||||
expected: []Silence{
|
||||
expected: []*models.Silence{
|
||||
global,
|
||||
ruleSilence1,
|
||||
ruleSilence2,
|
||||
@@ -59,7 +60,7 @@ func TestFilterByAccess(t *testing.T) {
|
||||
{
|
||||
name: "silence reader should get global + folder",
|
||||
user: newUser(ac.Permission{Action: silenceRead, Scope: folder1Scope}),
|
||||
expected: []Silence{
|
||||
expected: []*models.Silence{
|
||||
global,
|
||||
ruleSilence1,
|
||||
},
|
||||
@@ -71,8 +72,8 @@ func TestFilterByAccess(t *testing.T) {
|
||||
ac := &recordingAccessControlFake{}
|
||||
store := &fakeRuleUIDToNamespaceStore{
|
||||
Response: map[string]string{
|
||||
*ruleSilence1.RuleUID: folder1,
|
||||
*ruleSilence2.RuleUID: folder2,
|
||||
*ruleSilence1.GetRuleUID(): folder1,
|
||||
*ruleSilence2.GetRuleUID(): folder2,
|
||||
},
|
||||
}
|
||||
svc := NewSilenceService(ac, store)
|
||||
@@ -93,60 +94,60 @@ func TestFilterByAccess(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAuthorizeReadSilence(t *testing.T) {
|
||||
global := testSilence{ID: "global", RuleUID: nil}
|
||||
ruleSilence1 := testSilence{ID: "rule-1", RuleUID: utils.Pointer("rule-1-uid")}
|
||||
global := testSilence("global", nil)
|
||||
ruleSilence1 := testSilence("rule-1", utils.Pointer("rule-1-uid"))
|
||||
folder1 := "rule-1-folder-uid"
|
||||
folder1Scope := dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder1)
|
||||
ruleSilence2 := testSilence{ID: "rule-2", RuleUID: utils.Pointer("rule-2-uid")}
|
||||
ruleSilence2 := testSilence("rule-2", utils.Pointer("rule-2-uid"))
|
||||
folder2 := "rule-2-folder-uid"
|
||||
notFoundRule := testSilence{ID: "unknown-rule", RuleUID: utils.Pointer("unknown-rule-uid")}
|
||||
notFoundRule := testSilence("unknown-rule", utils.Pointer("unknown-rule-uid"))
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
user identity.Requester
|
||||
silence []testSilence
|
||||
silence []*models.Silence
|
||||
expectedErr error
|
||||
expectedDbAccess bool
|
||||
}{
|
||||
{
|
||||
name: "not authorized without permissions",
|
||||
user: newUser(),
|
||||
silence: []testSilence{global, ruleSilence1, notFoundRule},
|
||||
silence: []*models.Silence{global, ruleSilence1, notFoundRule},
|
||||
expectedErr: ErrAuthorizationBase,
|
||||
expectedDbAccess: false,
|
||||
},
|
||||
{
|
||||
name: "instance reader can read any silence",
|
||||
user: newUser(ac.Permission{Action: instancesRead}),
|
||||
silence: []testSilence{global, ruleSilence1, notFoundRule},
|
||||
silence: []*models.Silence{global, ruleSilence1, notFoundRule},
|
||||
expectedErr: nil,
|
||||
expectedDbAccess: false,
|
||||
},
|
||||
{
|
||||
name: "silence reader can read global",
|
||||
user: newUser(ac.Permission{Action: silenceRead, Scope: folder1Scope}),
|
||||
silence: []testSilence{global},
|
||||
silence: []*models.Silence{global},
|
||||
expectedErr: nil,
|
||||
expectedDbAccess: false,
|
||||
},
|
||||
{
|
||||
name: "silence reader can read from allowed folder",
|
||||
user: newUser(ac.Permission{Action: silenceRead, Scope: folder1Scope}),
|
||||
silence: []testSilence{ruleSilence1},
|
||||
silence: []*models.Silence{ruleSilence1},
|
||||
expectedErr: nil,
|
||||
expectedDbAccess: true,
|
||||
},
|
||||
{
|
||||
name: "silence reader cannot read from other folders",
|
||||
user: newUser(ac.Permission{Action: silenceRead, Scope: folder1Scope}),
|
||||
silence: []testSilence{ruleSilence2},
|
||||
silence: []*models.Silence{ruleSilence2},
|
||||
expectedErr: ErrAuthorizationBase,
|
||||
expectedDbAccess: true,
|
||||
},
|
||||
{
|
||||
name: "silence reader cannot read unknown rule",
|
||||
user: newUser(ac.Permission{Action: silenceRead, Scope: folder1Scope}),
|
||||
silence: []testSilence{notFoundRule},
|
||||
silence: []*models.Silence{notFoundRule},
|
||||
expectedErr: ErrAuthorizationBase,
|
||||
expectedDbAccess: true,
|
||||
},
|
||||
@@ -155,12 +156,12 @@ func TestAuthorizeReadSilence(t *testing.T) {
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
for _, silence := range testCase.silence {
|
||||
t.Run(silence.ID, func(t *testing.T) {
|
||||
t.Run(*silence.ID, func(t *testing.T) {
|
||||
ac := &recordingAccessControlFake{}
|
||||
store := &fakeRuleUIDToNamespaceStore{
|
||||
Response: map[string]string{
|
||||
*ruleSilence1.RuleUID: folder1,
|
||||
*ruleSilence2.RuleUID: folder2,
|
||||
*ruleSilence1.GetRuleUID(): folder1,
|
||||
*ruleSilence2.GetRuleUID(): folder2,
|
||||
},
|
||||
}
|
||||
svc := NewSilenceService(ac, store)
|
||||
@@ -183,16 +184,16 @@ func TestAuthorizeReadSilence(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAuthorizeCreateSilence(t *testing.T) {
|
||||
global := testSilence{ID: "global", RuleUID: nil}
|
||||
ruleSilence1 := testSilence{ID: "rule-1", RuleUID: utils.Pointer("rule-1-uid")}
|
||||
global := testSilence("global", nil)
|
||||
ruleSilence1 := testSilence("rule-1", utils.Pointer("rule-1-uid"))
|
||||
folder1 := "rule-1-folder-uid"
|
||||
folder1Scope := dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder1)
|
||||
ruleSilence2 := testSilence{ID: "rule-2", RuleUID: utils.Pointer("rule-2-uid")}
|
||||
ruleSilence2 := testSilence("rule-2", utils.Pointer("rule-2-uid"))
|
||||
folder2 := "rule-2-folder-uid"
|
||||
folder2Scope := dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder2)
|
||||
notFoundRule := testSilence{ID: "unknown-rule", RuleUID: utils.Pointer("unknown-rule-uid")}
|
||||
notFoundRule := testSilence("unknown-rule", utils.Pointer("unknown-rule-uid"))
|
||||
|
||||
silences := []testSilence{
|
||||
silences := []*models.Silence{
|
||||
global,
|
||||
ruleSilence1,
|
||||
ruleSilence2,
|
||||
@@ -208,7 +209,7 @@ func TestAuthorizeCreateSilence(t *testing.T) {
|
||||
user identity.Requester
|
||||
expectedErr error
|
||||
expectedDbAccess bool
|
||||
overrides map[testSilence]override
|
||||
overrides map[*models.Silence]override
|
||||
}{
|
||||
{
|
||||
name: "not authorized without permissions",
|
||||
@@ -243,7 +244,7 @@ func TestAuthorizeCreateSilence(t *testing.T) {
|
||||
{
|
||||
name: "instance read + silence create",
|
||||
user: newUser(ac.Permission{Action: silenceCreate, Scope: folder1Scope}, ac.Permission{Action: instancesRead}),
|
||||
overrides: map[testSilence]override{
|
||||
overrides: map[*models.Silence]override{
|
||||
global: {
|
||||
expectedErr: ErrAuthorizationBase,
|
||||
expectedDbAccess: false,
|
||||
@@ -259,7 +260,7 @@ func TestAuthorizeCreateSilence(t *testing.T) {
|
||||
{
|
||||
name: "silence read + instance create",
|
||||
user: newUser(ac.Permission{Action: silenceRead, Scope: folder1Scope}, ac.Permission{Action: instancesCreate}),
|
||||
overrides: map[testSilence]override{
|
||||
overrides: map[*models.Silence]override{
|
||||
global: {
|
||||
expectedErr: nil,
|
||||
expectedDbAccess: false,
|
||||
@@ -275,7 +276,7 @@ func TestAuthorizeCreateSilence(t *testing.T) {
|
||||
{
|
||||
name: "silence read + create",
|
||||
user: newUser(ac.Permission{Action: silenceRead, Scope: folder1Scope}, ac.Permission{Action: silenceCreate, Scope: folder1Scope}),
|
||||
overrides: map[testSilence]override{
|
||||
overrides: map[*models.Silence]override{
|
||||
global: {
|
||||
expectedErr: ErrAuthorizationBase,
|
||||
expectedDbAccess: false,
|
||||
@@ -299,12 +300,12 @@ func TestAuthorizeCreateSilence(t *testing.T) {
|
||||
expectedErr = s.expectedErr
|
||||
expectedDbAccess = s.expectedDbAccess
|
||||
}
|
||||
t.Run(silence.ID, func(t *testing.T) {
|
||||
t.Run(*silence.ID, func(t *testing.T) {
|
||||
ac := &recordingAccessControlFake{}
|
||||
store := &fakeRuleUIDToNamespaceStore{
|
||||
Response: map[string]string{
|
||||
*ruleSilence1.RuleUID: folder1,
|
||||
*ruleSilence2.RuleUID: folder2,
|
||||
*ruleSilence1.GetRuleUID(): folder1,
|
||||
*ruleSilence2.GetRuleUID(): folder2,
|
||||
},
|
||||
}
|
||||
svc := NewSilenceService(ac, store)
|
||||
@@ -328,16 +329,16 @@ func TestAuthorizeCreateSilence(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAuthorizeUpdateSilence(t *testing.T) {
|
||||
global := testSilence{ID: "global", RuleUID: nil}
|
||||
ruleSilence1 := testSilence{ID: "rule-1", RuleUID: utils.Pointer("rule-1-uid")}
|
||||
global := testSilence("global", nil)
|
||||
ruleSilence1 := testSilence("rule-1", utils.Pointer("rule-1-uid"))
|
||||
folder1 := "rule-1-folder-uid"
|
||||
folder1Scope := dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder1)
|
||||
ruleSilence2 := testSilence{ID: "rule-2", RuleUID: utils.Pointer("rule-2-uid")}
|
||||
ruleSilence2 := testSilence("rule-2", utils.Pointer("rule-2-uid"))
|
||||
folder2 := "rule-2-folder-uid"
|
||||
folder2Scope := dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder2)
|
||||
notFoundRule := testSilence{ID: "unknown-rule", RuleUID: utils.Pointer("unknown-rule-uid")}
|
||||
notFoundRule := testSilence("unknown-rule", utils.Pointer("unknown-rule-uid"))
|
||||
|
||||
silences := []testSilence{
|
||||
silences := []*models.Silence{
|
||||
global,
|
||||
ruleSilence1,
|
||||
ruleSilence2,
|
||||
@@ -353,7 +354,7 @@ func TestAuthorizeUpdateSilence(t *testing.T) {
|
||||
user identity.Requester
|
||||
expectedErr error
|
||||
expectedDbAccess bool
|
||||
overrides map[testSilence]override
|
||||
overrides map[*models.Silence]override
|
||||
}{
|
||||
{
|
||||
name: "not authorized without permissions",
|
||||
@@ -388,7 +389,7 @@ func TestAuthorizeUpdateSilence(t *testing.T) {
|
||||
{
|
||||
name: "instance read + silence write",
|
||||
user: newUser(ac.Permission{Action: silenceWrite, Scope: folder1Scope}, ac.Permission{Action: instancesRead}),
|
||||
overrides: map[testSilence]override{
|
||||
overrides: map[*models.Silence]override{
|
||||
global: {
|
||||
expectedErr: ErrAuthorizationBase,
|
||||
expectedDbAccess: false,
|
||||
@@ -404,7 +405,7 @@ func TestAuthorizeUpdateSilence(t *testing.T) {
|
||||
{
|
||||
name: "silence read + instance write",
|
||||
user: newUser(ac.Permission{Action: silenceRead, Scope: folder1Scope}, ac.Permission{Action: instancesWrite}),
|
||||
overrides: map[testSilence]override{
|
||||
overrides: map[*models.Silence]override{
|
||||
global: {
|
||||
expectedErr: nil,
|
||||
expectedDbAccess: false,
|
||||
@@ -420,7 +421,7 @@ func TestAuthorizeUpdateSilence(t *testing.T) {
|
||||
{
|
||||
name: "silence read + write",
|
||||
user: newUser(ac.Permission{Action: silenceRead, Scope: folder1Scope}, ac.Permission{Action: silenceWrite, Scope: folder1Scope}),
|
||||
overrides: map[testSilence]override{
|
||||
overrides: map[*models.Silence]override{
|
||||
global: {
|
||||
expectedErr: ErrAuthorizationBase,
|
||||
expectedDbAccess: false,
|
||||
@@ -444,12 +445,12 @@ func TestAuthorizeUpdateSilence(t *testing.T) {
|
||||
expectedErr = s.expectedErr
|
||||
expectedDbAccess = s.expectedDbAccess
|
||||
}
|
||||
t.Run(silence.ID, func(t *testing.T) {
|
||||
t.Run(*silence.ID, func(t *testing.T) {
|
||||
ac := &recordingAccessControlFake{}
|
||||
store := &fakeRuleUIDToNamespaceStore{
|
||||
Response: map[string]string{
|
||||
*ruleSilence1.RuleUID: folder1,
|
||||
*ruleSilence2.RuleUID: folder2,
|
||||
*ruleSilence1.GetRuleUID(): folder1,
|
||||
*ruleSilence2.GetRuleUID(): folder2,
|
||||
},
|
||||
}
|
||||
svc := NewSilenceService(ac, store)
|
||||
@@ -472,13 +473,8 @@ func TestAuthorizeUpdateSilence(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type testSilence struct {
|
||||
ID string
|
||||
RuleUID *string
|
||||
}
|
||||
|
||||
func (t testSilence) GetRuleUID() *string {
|
||||
return t.RuleUID
|
||||
func testSilence(id string, ruleUID *string) *models.Silence {
|
||||
return &models.Silence{ID: &id, RuleUID: ruleUID}
|
||||
}
|
||||
|
||||
type fakeRuleUIDToNamespaceStore struct {
|
||||
|
||||
Reference in New Issue
Block a user