RBAC: Remove folder guardians part 2 (#104645)

* replace usage of folder guardians with access control evaluators

* remove NewByFolderUID guardian

* bring up to date

* fix test

* more test fixes, and don't fetch the folder before evaluating lib element access

* change what error is returned

* fix alerting test

* try to fix linter errors

* replace the use of newByFolder guardian with direct access control evaluator checks

* remove newByFolder guardian

* remove unintentional changes

* remove unintentional changes

* undo unwanted change
pull/105576/head
Ieva 3 days ago committed by GitHub
parent c457a0c750
commit feaaf96269
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 23
      pkg/api/folder.go
  2. 36
      pkg/api/folder_test.go
  3. 4
      pkg/registry/apis/folders/register.go
  4. 22
      pkg/registry/apis/folders/sub_access.go
  5. 58
      pkg/services/folder/folderimpl/folder_unifiedstorage.go
  6. 60
      pkg/services/folder/folderimpl/folder_unifiedstorage_test.go

@ -19,7 +19,6 @@ import (
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/libraryelements/model"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/search"
@ -387,15 +386,16 @@ func (hs *HTTPServer) GetFolderDescendantCounts(c *contextmodel.ReqContext) resp
func (hs *HTTPServer) newToFolderDto(c *contextmodel.ReqContext, f *folder.Folder) (dtos.Folder, error) {
ctx := c.Req.Context()
toDTO := func(f *folder.Folder, checkCanView bool) (dtos.Folder, error) {
g, err := guardian.NewByFolder(c.Req.Context(), f, c.GetOrgID(), c.SignedInUser)
if err != nil {
return dtos.Folder{}, err
}
canEdit, _ := g.CanEdit()
canSave, _ := g.CanSave()
canAdmin, _ := g.CanAdmin()
canDelete, _ := g.CanDelete()
canEditEvaluator := accesscontrol.EvalPermission(dashboards.ActionFoldersWrite, dashboards.ScopeFoldersProvider.GetResourceScope(f.UID))
canEdit, _ := hs.AccessControl.Evaluate(ctx, c.SignedInUser, canEditEvaluator)
canSave := canEdit
canAdminEvaluator := accesscontrol.EvalAll(
accesscontrol.EvalPermission(dashboards.ActionFoldersPermissionsRead, dashboards.ScopeFoldersProvider.GetResourceScopeUID(f.UID)),
accesscontrol.EvalPermission(dashboards.ActionFoldersPermissionsWrite, dashboards.ScopeFoldersProvider.GetResourceScopeUID(f.UID)),
)
canAdmin, _ := hs.AccessControl.Evaluate(ctx, c.SignedInUser, canAdminEvaluator)
canDeleteEvaluator := accesscontrol.EvalPermission(dashboards.ActionFoldersDelete, dashboards.ScopeFoldersProvider.GetResourceScope(f.UID))
canDelete, _ := hs.AccessControl.Evaluate(ctx, c.SignedInUser, canDeleteEvaluator)
// Finding creator and last updater of the folder
updater, creator := anonString, anonString
@ -409,7 +409,8 @@ func (hs *HTTPServer) newToFolderDto(c *contextmodel.ReqContext, f *folder.Folde
acMetadata, _ := hs.getFolderACMetadata(c, f)
if checkCanView {
canView, _ := g.CanView()
canViewEvaluator := accesscontrol.EvalPermission(dashboards.ActionFoldersRead, dashboards.ScopeFoldersProvider.GetResourceScope(f.UID))
canView, _ := hs.AccessControl.Evaluate(ctx, c.SignedInUser, canViewEvaluator)
if !canView {
return dtos.Folder{
UID: REDACTED,

@ -25,7 +25,6 @@ import (
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/folder/foldertest"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/quota/quotatest"
"github.com/grafana/grafana/pkg/services/search/model"
"github.com/grafana/grafana/pkg/services/user"
@ -36,8 +35,6 @@ import (
func TestFoldersCreateAPIEndpoint(t *testing.T) {
folderService := &foldertest.FakeService{}
setUpRBACGuardian(t)
folderWithoutParentInput := "{ \"uid\": \"uid\", \"title\": \"Folder\"}"
type testCase struct {
@ -154,7 +151,6 @@ func TestFoldersCreateAPIEndpoint(t *testing.T) {
func TestFoldersUpdateAPIEndpoint(t *testing.T) {
folderService := &foldertest.FakeService{}
setUpRBACGuardian(t)
type testCase struct {
description string
@ -258,7 +254,6 @@ func testDescription(description string, expectedErr error) string {
}
func TestHTTPServer_FolderMetadata(t *testing.T) {
setUpRBACGuardian(t)
folderService := &foldertest.FakeService{}
features := featuremgmt.WithFeatures(featuremgmt.FlagNestedFolders)
server := SetupAPITestServer(t, func(hs *HTTPServer) {
@ -353,7 +348,6 @@ func TestFolderMoveAPIEndpoint(t *testing.T) {
folderService := &foldertest.FakeService{
ExpectedFolder: &folder.Folder{},
}
setUpRBACGuardian(t)
type testCase struct {
description string
@ -435,7 +429,6 @@ func TestFolderGetAPIEndpoint(t *testing.T) {
expectedParentOrgIDs []int64
expectedParentTitles []string
permissions []accesscontrol.Permission
g *guardian.FakeDashboardGuardian
}
tcs := []testCase{
{
@ -447,9 +440,8 @@ func TestFolderGetAPIEndpoint(t *testing.T) {
expectedParentOrgIDs: []int64{0, 0},
expectedParentTitles: []string{"parent title", "subfolder title"},
permissions: []accesscontrol.Permission{
{Action: dashboards.ActionFoldersRead, Scope: dashboards.ScopeFoldersProvider.GetResourceScopeUID("uid")},
{Action: dashboards.ActionFoldersRead, Scope: dashboards.ScopeFoldersProvider.GetResourceAllScope()},
},
g: &guardian.FakeDashboardGuardian{CanViewValue: true},
},
{
description: "get folder by UID should return parent folders redacted if nested folder are enabled and user does not have read access to parent folders",
@ -462,7 +454,6 @@ func TestFolderGetAPIEndpoint(t *testing.T) {
permissions: []accesscontrol.Permission{
{Action: dashboards.ActionFoldersRead, Scope: dashboards.ScopeFoldersProvider.GetResourceScopeUID("uid")},
},
g: &guardian.FakeDashboardGuardian{CanViewValue: false},
},
{
description: "get folder by UID should not return parent folders if nested folder are disabled",
@ -473,9 +464,8 @@ func TestFolderGetAPIEndpoint(t *testing.T) {
expectedParentOrgIDs: []int64{0, 0},
expectedParentTitles: []string{},
permissions: []accesscontrol.Permission{
{Action: dashboards.ActionFoldersRead, Scope: dashboards.ScopeFoldersProvider.GetResourceScopeUID("uid")},
{Action: dashboards.ActionFoldersRead, Scope: dashboards.ScopeFoldersProvider.GetResourceAllScope()},
},
g: &guardian.FakeDashboardGuardian{CanViewValue: true},
},
}
@ -487,13 +477,6 @@ func TestFolderGetAPIEndpoint(t *testing.T) {
})
t.Run(tc.description, func(t *testing.T) {
origNewGuardian := guardian.New
t.Cleanup(func() {
guardian.New = origNewGuardian
})
guardian.MockDashboardGuardian(tc.g)
req := srv.NewGetRequest(tc.URL)
req = webtest.RequestWithSignedInUser(req, userWithPermissions(1, tc.permissions))
resp, err := srv.Send(req)
@ -541,7 +524,7 @@ func TestGetFolderLegacyAndUnifiedStorage(t *testing.T) {
Title: legacyFolder.Title,
URL: legacyFolder.URL,
HasACL: false,
CanSave: false,
CanSave: true,
CanEdit: true,
CanAdmin: false,
CanDelete: false,
@ -614,8 +597,6 @@ func TestGetFolderLegacyAndUnifiedStorage(t *testing.T) {
for _, tc := range tcs {
t.Run(tc.description, func(t *testing.T) {
setUpRBACGuardian(t)
cfg := setting.NewCfg()
cfg.UnifiedStorage = map[string]setting.UnifiedStorageConfig{
folders.RESOURCEGROUP: {
@ -654,6 +635,7 @@ func TestGetFolderLegacyAndUnifiedStorage(t *testing.T) {
webtest.RequestWithSignedInUser(req, &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{
1: accesscontrol.GroupScopesByActionContext(context.Background(), []accesscontrol.Permission{
{Action: dashboards.ActionFoldersRead, Scope: dashboards.ScopeFoldersAll},
{Action: dashboards.ActionFoldersWrite, Scope: dashboards.ScopeFoldersAll},
}),
}})
@ -680,7 +662,6 @@ func TestGetFolderLegacyAndUnifiedStorage(t *testing.T) {
func TestSetDefaultPermissionsWhenCreatingFolder(t *testing.T) {
folderService := &foldertest.FakeService{}
setUpRBACGuardian(t)
folderWithoutParentInput := "{ \"uid\": \"uid\", \"title\": \"Folder\"}"
type testCase struct {
@ -768,12 +749,3 @@ func TestSetDefaultPermissionsWhenCreatingFolder(t *testing.T) {
})
}
}
func setUpRBACGuardian(t *testing.T) {
origNewGuardian := guardian.New
t.Cleanup(func() {
guardian.New = origNewGuardian
})
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanEditValue: true, CanViewValue: true})
}

@ -52,6 +52,7 @@ type FolderAPIBuilder struct {
folderSvc folder.Service
folderPermissionsSvc accesscontrol.FolderPermissionsService
acService accesscontrol.Service
ac accesscontrol.AccessControl
storage grafanarest.Storage
authorizer authorizer.Authorizer
@ -78,6 +79,7 @@ func RegisterAPIService(cfg *setting.Cfg,
folderSvc: folderSvc,
folderPermissionsSvc: folderPermissionsSvc,
acService: acService,
ac: accessControl,
cfg: cfg,
authorizer: newLegacyAuthorizer(accessControl),
searcher: unified,
@ -184,7 +186,7 @@ func (b *FolderAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.API
getter: storage[resourceInfo.StoragePath()].(rest.Getter), // Get the parents
}
storage[resourceInfo.StoragePath("counts")] = &subCountREST{searcher: b.searcher}
storage[resourceInfo.StoragePath("access")] = &subAccessREST{b.folderSvc}
storage[resourceInfo.StoragePath("access")] = &subAccessREST{b.folderSvc, b.ac}
apiGroupInfo.VersionedResourcesStorageMap[folders.VERSION] = storage
b.storage = storage[resourceInfo.StoragePath()].(grafanarest.Storage)

@ -9,13 +9,15 @@ import (
folders "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/guardian"
)
type subAccessREST struct {
service folder.Service
ac accesscontrol.AccessControl
}
var _ = rest.Connecter(&subAccessREST{})
@ -63,17 +65,19 @@ func (r *subAccessREST) Connect(ctx context.Context, name string, opts runtime.O
if err != nil {
return nil, err
}
guardian, err := guardian.NewByFolder(ctx, f, ns.OrgID, user)
if err != nil {
return nil, err
}
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
access := &folders.FolderAccessInfo{}
access.CanEdit, _ = guardian.CanEdit()
access.CanSave, _ = guardian.CanSave()
access.CanAdmin, _ = guardian.CanAdmin()
access.CanDelete, _ = guardian.CanDelete()
canEditEvaluator := accesscontrol.EvalPermission(dashboards.ActionFoldersWrite, dashboards.ScopeFoldersProvider.GetResourceScope(f.UID))
access.CanEdit, _ = r.ac.Evaluate(ctx, user, canEditEvaluator)
access.CanSave = access.CanEdit
canAdminEvaluator := accesscontrol.EvalAll(
accesscontrol.EvalPermission(dashboards.ActionFoldersPermissionsRead, dashboards.ScopeFoldersProvider.GetResourceScopeUID(f.UID)),
accesscontrol.EvalPermission(dashboards.ActionFoldersPermissionsWrite, dashboards.ScopeFoldersProvider.GetResourceScopeUID(f.UID)),
)
access.CanAdmin, _ = r.ac.Evaluate(ctx, user, canAdminEvaluator)
canDeleteEvaluator := accesscontrol.EvalPermission(dashboards.ActionFoldersDelete, dashboards.ScopeFoldersProvider.GetResourceScope(f.UID))
access.CanDelete, _ = r.ac.Evaluate(ctx, user, canDeleteEvaluator)
responder.Object(http.StatusOK, access)
}), nil
}

@ -26,7 +26,6 @@ import (
dashboardsearch "github.com/grafana/grafana/pkg/services/dashboards/service/search"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/guardian"
"github.com/grafana/grafana/pkg/services/search/model"
"github.com/grafana/grafana/pkg/services/store/entity"
"github.com/grafana/grafana/pkg/storage/unified/resource"
@ -123,15 +122,8 @@ func (s *Service) getFromApiServer(ctx context.Context, q *folder.GetFolderQuery
return dashFolder, nil
}
// do not get guardian by the folder ID because it differs from the nested folder ID
// and the legacy folder ID has been associated with the permissions:
// use the folde UID instead that is the same for both
g, err := guardian.NewByFolder(ctx, dashFolder, dashFolder.OrgID, q.SignedInUser)
if err != nil {
return nil, err
}
if canView, err := g.CanView(); err != nil || !canView {
evaluator := accesscontrol.EvalPermission(dashboards.ActionFoldersRead, dashboards.ScopeFoldersProvider.GetResourceScopeUID(dashFolder.UID))
if canView, err := s.accessControl.Evaluate(ctx, q.SignedInUser, evaluator); err != nil || !canView {
if err != nil {
return nil, toFolderError(err)
}
@ -385,28 +377,16 @@ func (s *Service) getChildrenFromApiServer(ctx context.Context, q *folder.GetChi
return s.getRootFoldersFromApiServer(ctx, q)
}
var err error
// TODO: figure out what to do with Guardian
// we only need to check access to the folder
// if the parent is accessible then the subfolders are accessible as well (due to inheritance)
f := &folder.Folder{
UID: q.UID,
}
g, err := guardian.NewByFolder(ctx, f, q.OrgID, q.SignedInUser)
if err != nil {
return nil, err
}
guardianFunc := g.CanView
evaluator := accesscontrol.EvalPermission(dashboards.ActionFoldersRead, dashboards.ScopeFoldersProvider.GetResourceScopeUID(q.UID))
if q.Permission == dashboardaccess.PERMISSION_EDIT {
guardianFunc = g.CanEdit
}
hasAccess, err := guardianFunc()
if err != nil {
return nil, err
evaluator = accesscontrol.EvalPermission(dashboards.ActionFoldersWrite, dashboards.ScopeFoldersProvider.GetResourceScopeUID(q.UID))
}
if !hasAccess {
if hasAccess, err := s.accessControl.Evaluate(ctx, q.SignedInUser, evaluator); err != nil || !hasAccess {
if err != nil {
return nil, err
}
return nil, dashboards.ErrFolderAccessDenied
}
@ -565,15 +545,8 @@ func (s *Service) updateOnApiServer(ctx context.Context, cmd *folder.UpdateFolde
return nil, dashboards.ErrDashboardTitleEmpty
}
f := &folder.Folder{
UID: cmd.UID,
}
g, err := guardian.NewByFolder(ctx, f, cmd.OrgID, cmd.SignedInUser)
if err != nil {
return nil, err
}
if canSave, err := g.CanSave(); err != nil || !canSave {
evaluator := accesscontrol.EvalPermission(dashboards.ActionFoldersWrite, dashboards.ScopeFoldersProvider.GetResourceScopeUID(cmd.UID))
if hasAccess, err := s.accessControl.Evaluate(ctx, cmd.SignedInUser, evaluator); err != nil || !hasAccess {
if err != nil {
return nil, err
}
@ -624,15 +597,8 @@ func (s *Service) deleteFromApiServer(ctx context.Context, cmd *folder.DeleteFol
return folder.ErrBadRequest.Errorf("invalid orgID")
}
f := &folder.Folder{
UID: cmd.UID,
}
guard, err := guardian.NewByFolder(ctx, f, cmd.OrgID, cmd.SignedInUser)
if err != nil {
return err
}
if canSave, err := guard.CanDelete(); err != nil || !canSave {
evaluator := accesscontrol.EvalPermission(dashboards.ActionFoldersDelete, dashboards.ScopeFoldersProvider.GetResourceScopeUID(cmd.UID))
if hasAccess, err := s.accessControl.Evaluate(ctx, cmd.SignedInUser, evaluator); err != nil || !hasAccess {
if err != nil {
return toFolderError(err)
}

@ -32,7 +32,6 @@ import (
dashboardsearch "github.com/grafana/grafana/pkg/services/dashboards/service/search"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/guardian"
ngstore "github.com/grafana/grafana/pkg/services/ngalert/store"
"github.com/grafana/grafana/pkg/services/publicdashboards"
"github.com/grafana/grafana/pkg/services/search/model"
@ -182,11 +181,6 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
folderApiServerMock := httptest.NewServer(mux)
defer folderApiServerMock.Close()
origNewGuardian := guardian.New
t.Cleanup(func() {
guardian.New = origNewGuardian
})
db, cfg := sqlstore.InitTestDB(t)
cfg.AppURL = folderApiServerMock.URL
@ -213,6 +207,8 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
[]accesscontrol.Permission{
{Action: dashboards.ActionFoldersCreate, Scope: dashboards.ScopeFoldersAll},
{Action: dashboards.ActionFoldersWrite, Scope: dashboards.ScopeFoldersAll},
{Action: dashboards.ActionFoldersDelete, Scope: dashboards.ScopeFoldersAll},
{Action: dashboards.ActionFoldersRead, Scope: dashboards.ScopeFoldersAll},
{Action: accesscontrol.ActionAlertingRuleDelete, Scope: dashboards.ScopeFoldersAll},
}),
}}
@ -245,9 +241,6 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
t.Run("Folder service tests", func(t *testing.T) {
t.Run("Given user has no permissions", func(t *testing.T) {
origNewGuardian := guardian.New
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{})
ctx = identity.WithRequester(context.Background(), noPermUsr)
f := folder.NewFolder("Folder", "")
@ -303,16 +296,9 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
require.Error(t, err)
require.Equal(t, dashboards.ErrFolderAccessDenied, err)
})
t.Cleanup(func() {
guardian.New = origNewGuardian
})
})
t.Run("Given user has permission to save", func(t *testing.T) {
origNewGuardian := guardian.New
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanSaveValue: true, CanViewValue: true})
ctx = identity.WithRequester(context.Background(), usr)
f := &folder.Folder{
@ -406,16 +392,9 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
})
require.NoError(t, err)
})
t.Cleanup(func() {
guardian.New = origNewGuardian
})
})
t.Run("Given user has permission to view", func(t *testing.T) {
origNewGuardian := guardian.New
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanViewValue: true})
t.Run("When get folder by uid should return folder", func(t *testing.T) {
actual, err := folderService.Get(ctx, &folder.GetFolderQuery{
UID: &fooFolder.UID,
@ -507,10 +486,6 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
require.Nil(t, actual)
require.ErrorIs(t, err, dashboards.ErrFolderNotFound)
})
t.Cleanup(func() {
guardian.New = origNewGuardian
})
})
t.Run("Returns root folder", func(t *testing.T) {
@ -535,10 +510,6 @@ func TestIntegrationFolderServiceViaUnifiedStorage(t *testing.T) {
func TestSearchFoldersFromApiServer(t *testing.T) {
fakeK8sClient := new(client.MockK8sHandler)
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
CanSaveValue: true,
CanViewValue: true,
})
folderStore := folder.NewFakeStore()
folderStore.ExpectedFolder = &folder.Folder{
UID: "parent-uid",
@ -546,10 +517,11 @@ func TestSearchFoldersFromApiServer(t *testing.T) {
Title: "parent title",
}
service := Service{
k8sclient: fakeK8sClient,
features: featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders),
unifiedStore: folderStore,
tracer: tracing.NewNoopTracerService(),
k8sclient: fakeK8sClient,
features: featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders),
unifiedStore: folderStore,
tracer: tracing.NewNoopTracerService(),
accessControl: actest.FakeAccessControl{ExpectedEvaluate: true},
}
user := &user.SignedInUser{OrgID: 1}
ctx := identity.WithRequester(context.Background(), user)
@ -780,10 +752,6 @@ func TestSearchFoldersFromApiServer(t *testing.T) {
func TestGetFoldersFromApiServer(t *testing.T) {
fakeK8sClient := new(client.MockK8sHandler)
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
CanSaveValue: true,
CanViewValue: true,
})
folderStore := folder.NewFakeStore()
folderStore.ExpectedFolder = &folder.Folder{
UID: "parent-uid",
@ -791,10 +759,11 @@ func TestGetFoldersFromApiServer(t *testing.T) {
Title: "parent title",
}
service := Service{
k8sclient: fakeK8sClient,
features: featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders),
unifiedStore: folderStore,
tracer: tracing.NewNoopTracerService(),
k8sclient: fakeK8sClient,
features: featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders),
unifiedStore: folderStore,
accessControl: actest.FakeAccessControl{ExpectedEvaluate: true},
tracer: tracing.NewNoopTracerService(),
}
user := &user.SignedInUser{OrgID: 1}
ctx := identity.WithRequester(context.Background(), user)
@ -882,16 +851,13 @@ func TestDeleteFoldersFromApiServer(t *testing.T) {
unifiedStore: fakeFolderStore,
dashboardStore: dashboardStore,
publicDashboardService: publicDashboardFakeService,
accessControl: actest.FakeAccessControl{ExpectedEvaluate: true},
registry: make(map[string]folder.RegistryService),
features: featuremgmt.WithFeatures(featuremgmt.FlagKubernetesClientDashboardsFolders),
tracer: tracing.NewNoopTracerService(),
}
user := &user.SignedInUser{OrgID: 1}
ctx := identity.WithRequester(context.Background(), user)
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
CanSaveValue: true,
CanViewValue: true,
})
db, cfg := sqlstore.InitTestDB(t)
alertingStore := ngstore.DBstore{

Loading…
Cancel
Save