merge main

This commit is contained in:
Ryan McKinley
2024-06-20 17:05:36 +03:00
60 changed files with 1394 additions and 1507 deletions
+4 -6
View File
@@ -21,25 +21,23 @@ import (
"github.com/grafana/grafana/pkg/setting"
)
type ZanzanaClient interface{}
// ProvideZanzana used to register ZanzanaClient.
// It will also start an embedded ZanzanaSever if mode is set to "embedded".
func ProvideZanzana(cfg *setting.Cfg, db db.DB, features featuremgmt.FeatureToggles) (ZanzanaClient, error) {
func ProvideZanzana(cfg *setting.Cfg, db db.DB, features featuremgmt.FeatureToggles) (zanzana.Client, error) {
if !features.IsEnabledGlobally(featuremgmt.FlagZanzana) {
return zanzana.NoopClient{}, nil
}
logger := log.New("zanzana")
var client *zanzana.Client
var client zanzana.Client
switch cfg.Zanzana.Mode {
case setting.ZanzanaModeClient:
conn, err := grpc.NewClient(cfg.Zanzana.Addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return nil, fmt.Errorf("failed to create zanzana client to remote server: %w", err)
}
client = zanzana.NewClient(openfgav1.NewOpenFGAServiceClient(conn))
client = zanzana.NewClient(conn)
case setting.ZanzanaModeEmbedded:
store, err := zanzana.NewEmbeddedStore(cfg, db, logger)
if err != nil {
@@ -53,7 +51,7 @@ func ProvideZanzana(cfg *setting.Cfg, db db.DB, features featuremgmt.FeatureTogg
channel := &inprocgrpc.Channel{}
openfgav1.RegisterOpenFGAServiceServer(channel, srv)
client = zanzana.NewClient(openfgav1.NewOpenFGAServiceClient(channel))
client = zanzana.NewClient(channel)
default:
return nil, fmt.Errorf("unsupported zanzana mode: %s", cfg.Zanzana.Mode)
}
+35 -5
View File
@@ -1,16 +1,46 @@
package zanzana
import (
"context"
openfgav1 "github.com/openfga/api/proto/openfga/v1"
"google.golang.org/grpc"
"github.com/grafana/grafana/pkg/infra/log"
)
// FIXME(kalleep): Build out our wrapper client for openFGA
type Client struct {
c openfgav1.OpenFGAServiceClient
// Client is a wrapper around OpenFGAServiceClient with only methods using in Grafana included.
type Client interface {
Check(ctx context.Context, in *openfgav1.CheckRequest, opts ...grpc.CallOption) (*openfgav1.CheckResponse, error)
ListObjects(ctx context.Context, in *openfgav1.ListObjectsRequest, opts ...grpc.CallOption) (*openfgav1.ListObjectsResponse, error)
}
func NewClient(c openfgav1.OpenFGAServiceClient) *Client {
return &Client{c}
type zanzanaClient struct {
client openfgav1.OpenFGAServiceClient
logger log.Logger
}
func NewClient(cc grpc.ClientConnInterface) Client {
return &zanzanaClient{
client: openfgav1.NewOpenFGAServiceClient(cc),
logger: log.New("zanzana-client"),
}
}
func (c *zanzanaClient) Check(ctx context.Context, in *openfgav1.CheckRequest, opts ...grpc.CallOption) (*openfgav1.CheckResponse, error) {
return c.client.Check(ctx, in, opts...)
}
func (c *zanzanaClient) ListObjects(ctx context.Context, in *openfgav1.ListObjectsRequest, opts ...grpc.CallOption) (*openfgav1.ListObjectsResponse, error) {
return c.client.ListObjects(ctx, in, opts...)
}
type NoopClient struct{}
func (nc NoopClient) Check(ctx context.Context, in *openfgav1.CheckRequest, opts ...grpc.CallOption) (*openfgav1.CheckResponse, error) {
return nil, nil
}
func (nc NoopClient) ListObjects(ctx context.Context, in *openfgav1.ListObjectsRequest, opts ...grpc.CallOption) (*openfgav1.ListObjectsResponse, error) {
return nil, nil
}
+1
View File
@@ -18,4 +18,5 @@ var (
ErrDataSourceNameInvalid = errutil.ValidationFailed("datasource.nameInvalid", errutil.WithPublicMessage("Invalid datasource name."))
ErrDataSourceURLInvalid = errutil.ValidationFailed("datasource.urlInvalid", errutil.WithPublicMessage("Invalid datasource url."))
ErrDataSourceAPIVersionInvalid = errutil.ValidationFailed("datasource.apiVersionInvalid", errutil.WithPublicMessage("Invalid datasource apiVersion."))
ErrDataSourceUIDInvalid = errutil.ValidationFailed("datasource.uidInvalid", errutil.WithPublicMessage("Invalid datasource UID."))
)
+4 -4
View File
@@ -252,8 +252,8 @@ func (ss *SqlStore) AddDataSource(ctx context.Context, cmd *datasources.AddDataS
cmd.UID = uid
} else if err := util.ValidateUID(cmd.UID); err != nil {
logDeprecatedInvalidDsUid(ss.logger, cmd.UID, cmd.Name, "create", err)
if ss.features != nil && ss.features.IsEnabled(ctx, featuremgmt.FlagAutofixDSUID) {
return fmt.Errorf("invalid UID for datasource %s: %w", cmd.Name, err)
if ss.features != nil && ss.features.IsEnabled(ctx, featuremgmt.FlagFailWrongDSUID) {
return datasources.ErrDataSourceUIDInvalid.Errorf("invalid UID for datasource %s: %w", cmd.Name, err)
}
}
@@ -329,8 +329,8 @@ func (ss *SqlStore) UpdateDataSource(ctx context.Context, cmd *datasources.Updat
if cmd.UID != "" {
if err := util.ValidateUID(cmd.UID); err != nil {
logDeprecatedInvalidDsUid(ss.logger, cmd.UID, cmd.Name, "update", err)
if ss.features != nil && ss.features.IsEnabled(ctx, featuremgmt.FlagAutofixDSUID) {
cmd.UID = util.AutofixUID(cmd.UID)
if ss.features != nil && ss.features.IsEnabled(ctx, featuremgmt.FlagFailWrongDSUID) {
return datasources.ErrDataSourceUIDInvalid.Errorf("invalid UID for datasource %s: %w", cmd.Name, err)
}
}
}
+5 -12
View File
@@ -104,7 +104,7 @@ func TestIntegrationDataAccess(t *testing.T) {
ss := SqlStore{
db: db,
logger: log.NewNopLogger(),
features: featuremgmt.WithFeatures(featuremgmt.FlagAutofixDSUID),
features: featuremgmt.WithFeatures(featuremgmt.FlagFailWrongDSUID),
}
cmd := defaultAddDatasourceCommand
cmd.UID = "test/uid"
@@ -232,28 +232,21 @@ func TestIntegrationDataAccess(t *testing.T) {
require.NoError(t, err)
})
t.Run("updates UID with a valid one", func(t *testing.T) {
t.Run("fails to update a datasource with an invalid uid", func(t *testing.T) {
db := db.InitTestDB(t)
ds := initDatasource(db)
ss := SqlStore{
db: db,
logger: log.NewNopLogger(),
features: featuremgmt.WithFeatures(featuremgmt.FlagAutofixDSUID),
features: featuremgmt.WithFeatures(featuremgmt.FlagFailWrongDSUID),
}
require.NotEmpty(t, ds.UID)
cmd := defaultUpdateDatasourceCommand
cmd.ID = ds.ID
cmd.UID = "new/uid"
res, err := ss.UpdateDataSource(context.Background(), &cmd)
require.NoError(t, err)
require.Equal(t, "new-uid", res.UID)
// Return the datasource with the valid UID
query := datasources.GetDataSourceQuery{UID: "new-uid", OrgID: 10}
dataSource, err := ss.GetDataSource(context.Background(), &query)
require.NoError(t, err)
require.Equal(t, "new-uid", dataSource.UID)
_, err := ss.UpdateDataSource(context.Background(), &cmd)
require.ErrorContains(t, err, "invalid format of UID")
})
})
+6 -13
View File
@@ -480,13 +480,6 @@ var (
FrontendOnly: true,
Owner: grafanaDashboardsSquad,
},
{
Name: "prometheusIncrementalQueryInstrumentation",
Description: "Adds RudderStack events to incremental queries",
FrontendOnly: true,
Stage: FeatureStageExperimental,
Owner: grafanaObservabilityMetricsSquad,
},
{
Name: "logsExploreTableVisualisation",
Description: "A table visualisation for logs in Explore",
@@ -1222,12 +1215,6 @@ var (
FrontendOnly: false,
AllowSelfServe: false,
},
{
Name: "autofixDSUID",
Description: "Automatically migrates invalid datasource UIDs",
Stage: FeatureStageExperimental,
Owner: grafanaPluginsPlatformSquad,
},
{
Name: "logsExploreTableDefaultVisualization",
Description: "Sets the logs table as default visualisation in logs explore",
@@ -1336,6 +1323,12 @@ var (
HideFromDocs: true,
HideFromAdminPage: true,
},
{
Name: "failWrongDSUID",
Description: "Throws an error if a datasource has an invalid UIDs",
Stage: FeatureStageExperimental,
Owner: grafanaPluginsPlatformSquad,
},
{
Name: "databaseReadReplica",
Description: "Use a read replica for some database queries.",
+1 -2
View File
@@ -63,7 +63,6 @@ frontendSandboxMonitorOnly,experimental,@grafana/plugins-platform-backend,false,
sqlDatasourceDatabaseSelection,preview,@grafana/dataviz-squad,false,false,true
recordedQueriesMulti,GA,@grafana/observability-metrics,false,false,false
vizAndWidgetSplit,experimental,@grafana/dashboards-squad,false,false,true
prometheusIncrementalQueryInstrumentation,experimental,@grafana/observability-metrics,false,false,true
logsExploreTableVisualisation,GA,@grafana/observability-logs,false,false,true
awsDatasourcesTempCredentials,experimental,@grafana/aws-datasources,false,false,false
transformationsRedesign,GA,@grafana/observability-metrics,false,false,true
@@ -161,7 +160,6 @@ accessActionSets,experimental,@grafana/identity-access-team,false,false,false
disableNumericMetricsSortingInExpressions,experimental,@grafana/observability-metrics,false,true,false
grafanaManagedRecordingRules,experimental,@grafana/alerting-squad,false,false,false
queryLibrary,experimental,@grafana/explore-squad,false,false,false
autofixDSUID,experimental,@grafana/plugins-platform-backend,false,false,false
logsExploreTableDefaultVisualization,experimental,@grafana/observability-logs,false,false,true
newDashboardSharingComponent,experimental,@grafana/sharing-squad,false,false,true
alertingListViewV2,experimental,@grafana/alerting-squad,false,false,true
@@ -177,5 +175,6 @@ pinNavItems,experimental,@grafana/grafana-frontend-platform,false,false,false
authZGRPCServer,experimental,@grafana/identity-access-team,false,false,false
openSearchBackendFlowEnabled,preview,@grafana/aws-datasources,false,false,false
ssoSettingsLDAP,experimental,@grafana/identity-access-team,false,false,false
failWrongDSUID,experimental,@grafana/plugins-platform-backend,false,false,false
databaseReadReplica,experimental,@grafana/grafana-backend-services-squad,false,false,false
zanzana,experimental,@grafana/identity-access-team,false,false,false
1 Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
63 sqlDatasourceDatabaseSelection preview @grafana/dataviz-squad false false true
64 recordedQueriesMulti GA @grafana/observability-metrics false false false
65 vizAndWidgetSplit experimental @grafana/dashboards-squad false false true
prometheusIncrementalQueryInstrumentation experimental @grafana/observability-metrics false false true
66 logsExploreTableVisualisation GA @grafana/observability-logs false false true
67 awsDatasourcesTempCredentials experimental @grafana/aws-datasources false false false
68 transformationsRedesign GA @grafana/observability-metrics false false true
160 disableNumericMetricsSortingInExpressions experimental @grafana/observability-metrics false true false
161 grafanaManagedRecordingRules experimental @grafana/alerting-squad false false false
162 queryLibrary experimental @grafana/explore-squad false false false
autofixDSUID experimental @grafana/plugins-platform-backend false false false
163 logsExploreTableDefaultVisualization experimental @grafana/observability-logs false false true
164 newDashboardSharingComponent experimental @grafana/sharing-squad false false true
165 alertingListViewV2 experimental @grafana/alerting-squad false false true
175 authZGRPCServer experimental @grafana/identity-access-team false false false
176 openSearchBackendFlowEnabled preview @grafana/aws-datasources false false false
177 ssoSettingsLDAP experimental @grafana/identity-access-team false false false
178 failWrongDSUID experimental @grafana/plugins-platform-backend false false false
179 databaseReadReplica experimental @grafana/grafana-backend-services-squad false false false
180 zanzana experimental @grafana/identity-access-team false false false
+4 -8
View File
@@ -263,10 +263,6 @@ const (
// Split panels between visualizations and widgets
FlagVizAndWidgetSplit = "vizAndWidgetSplit"
// FlagPrometheusIncrementalQueryInstrumentation
// Adds RudderStack events to incremental queries
FlagPrometheusIncrementalQueryInstrumentation = "prometheusIncrementalQueryInstrumentation"
// FlagLogsExploreTableVisualisation
// A table visualisation for logs in Explore
FlagLogsExploreTableVisualisation = "logsExploreTableVisualisation"
@@ -655,10 +651,6 @@ const (
// Enables Query Library feature in Explore
FlagQueryLibrary = "queryLibrary"
// FlagAutofixDSUID
// Automatically migrates invalid datasource UIDs
FlagAutofixDSUID = "autofixDSUID"
// FlagLogsExploreTableDefaultVisualization
// Sets the logs table as default visualisation in logs explore
FlagLogsExploreTableDefaultVisualization = "logsExploreTableDefaultVisualization"
@@ -719,6 +711,10 @@ const (
// Use the new SSO Settings API to configure LDAP
FlagSsoSettingsLDAP = "ssoSettingsLDAP"
// FlagFailWrongDSUID
// Throws an error if a datasource has an invalid UIDs
FlagFailWrongDSUID = "failWrongDSUID"
// FlagDatabaseReadReplica
// Use a read replica for some database queries.
FlagDatabaseReadReplica = "databaseReadReplica"
+17 -3
View File
@@ -401,8 +401,9 @@
{
"metadata": {
"name": "autofixDSUID",
"resourceVersion": "1718727528075",
"creationTimestamp": "2024-05-03T11:32:07Z"
"resourceVersion": "1717578796182",
"creationTimestamp": "2024-05-03T11:32:07Z",
"deletionTimestamp": "2024-06-18T14:28:32Z"
},
"spec": {
"description": "Automatically migrates invalid datasource UIDs",
@@ -915,6 +916,18 @@
"frontend": true
}
},
{
"metadata": {
"name": "failWrongDSUID",
"resourceVersion": "1718721033692",
"creationTimestamp": "2024-06-18T14:30:33Z"
},
"spec": {
"description": "Throws an error if a datasource has an invalid UIDs",
"stage": "experimental",
"codeowner": "@grafana/plugins-platform-backend"
}
},
{
"metadata": {
"name": "faroDatasourceSelector",
@@ -1819,7 +1832,8 @@
"metadata": {
"name": "prometheusIncrementalQueryInstrumentation",
"resourceVersion": "1718727528075",
"creationTimestamp": "2023-07-05T19:39:49Z"
"creationTimestamp": "2023-07-05T19:39:49Z",
"deletionTimestamp": "2024-06-20T11:30:37Z"
},
"spec": {
"description": "Adds RudderStack events to incremental queries",
@@ -5,7 +5,6 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity"
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/store"
)
@@ -22,8 +21,8 @@ type FakeRuleService struct {
AuthorizeDatasourceAccessForRuleGroupFunc func(context.Context, identity.Requester, models.RulesGroup) error
HasAccessToRuleGroupFunc func(context.Context, identity.Requester, models.RulesGroup) (bool, error)
AuthorizeAccessToRuleGroupFunc func(context.Context, identity.Requester, models.RulesGroup) error
HasAccessInFolderFunc func(context.Context, identity.Requester, accesscontrol.Namespaced) (bool, error)
AuthorizeAccessInFolderFunc func(context.Context, identity.Requester, accesscontrol.Namespaced) error
HasAccessInFolderFunc func(context.Context, identity.Requester, models.Namespaced) (bool, error)
AuthorizeAccessInFolderFunc func(context.Context, identity.Requester, models.Namespaced) error
AuthorizeRuleChangesFunc func(context.Context, identity.Requester, *store.GroupDelta) error
Calls []Call
@@ -77,7 +76,7 @@ func (s *FakeRuleService) AuthorizeAccessToRuleGroup(ctx context.Context, user i
return nil
}
func (s *FakeRuleService) HasAccessInFolder(ctx context.Context, user identity.Requester, namespaced accesscontrol.Namespaced) (bool, error) {
func (s *FakeRuleService) HasAccessInFolder(ctx context.Context, user identity.Requester, namespaced models.Namespaced) (bool, error) {
s.Calls = append(s.Calls, Call{"HasAccessInFolder", []interface{}{ctx, user, namespaced}})
if s.HasAccessInFolderFunc != nil {
return s.HasAccessInFolderFunc(ctx, user, namespaced)
@@ -85,7 +84,7 @@ func (s *FakeRuleService) HasAccessInFolder(ctx context.Context, user identity.R
return false, nil
}
func (s *FakeRuleService) AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, namespaced accesscontrol.Namespaced) error {
func (s *FakeRuleService) AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, namespaced models.Namespaced) error {
s.Calls = append(s.Calls, Call{"AuthorizeAccessInFolder", []interface{}{ctx, user, namespaced}})
if s.AuthorizeAccessInFolderFunc != nil {
return s.AuthorizeAccessInFolderFunc(ctx, user, namespaced)
+2 -6
View File
@@ -31,10 +31,6 @@ func NewRuleService(ac accesscontrol.AccessControl) *RuleService {
}
}
type Namespaced interface {
GetNamespaceUID() string
}
// getReadFolderAccessEvaluator constructs accesscontrol.Evaluator that checks all permissions required to read rules in specific folder
func getReadFolderAccessEvaluator(folderUID string) accesscontrol.Evaluator {
return accesscontrol.EvalAll(
@@ -130,7 +126,7 @@ func (r *RuleService) AuthorizeAccessToRuleGroup(ctx context.Context, user ident
// - ("folders:read") read the folder
// - ("alert.rules:read") read alert rules in the folder
// Returns false if the requester does not have enough permissions, and error if something went wrong during the permission evaluation.
func (r *RuleService) HasAccessInFolder(ctx context.Context, user identity.Requester, rule Namespaced) (bool, error) {
func (r *RuleService) HasAccessInFolder(ctx context.Context, user identity.Requester, rule models.Namespaced) (bool, error) {
eval := accesscontrol.EvalAll(getReadFolderAccessEvaluator(rule.GetNamespaceUID()))
return r.HasAccess(ctx, user, eval)
}
@@ -140,7 +136,7 @@ func (r *RuleService) HasAccessInFolder(ctx context.Context, user identity.Reque
// - ("folders:read") read the folder
// - ("alert.rules:read") read alert rules in the folder
// Returns error if at least one permission is missing or if something went wrong during the permission evaluation
func (r *RuleService) AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, rule Namespaced) error {
func (r *RuleService) AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, rule models.Namespaced) error {
eval := accesscontrol.EvalAll(getReadFolderAccessEvaluator(rule.GetNamespaceUID()))
return r.HasAccessOrError(ctx, user, eval, func() string {
return fmt.Sprintf("access rules in folder '%s'", rule.GetNamespaceUID())
+1 -1
View File
@@ -45,7 +45,7 @@ type RuleAccessControlService interface {
AuthorizeRuleChanges(ctx context.Context, user identity.Requester, change *store.GroupDelta) error
AuthorizeDatasourceAccessForRule(ctx context.Context, user identity.Requester, rule *models.AlertRule) error
AuthorizeDatasourceAccessForRuleGroup(ctx context.Context, user identity.Requester, rules models.RulesGroup) error
AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, namespaced accesscontrol.Namespaced) error
AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, namespaced models.Namespaced) error
}
// API handlers.
+1 -2
View File
@@ -11,7 +11,6 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity"
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
"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"
@@ -147,7 +146,7 @@ func (f fakeRuleAccessControlService) AuthorizeAccessToRuleGroup(ctx context.Con
return nil
}
func (f fakeRuleAccessControlService) AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, namespaced accesscontrol.Namespaced) error {
func (f fakeRuleAccessControlService) AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, namespaced models.Namespaced) error {
return nil
}
@@ -269,6 +269,11 @@ type AlertRule struct {
NotificationSettings []NotificationSettings `xorm:"notification_settings"` // we use slice to workaround xorm mapping that does not serialize a struct to JSON unless it's a slice
}
// Namespaced describes a class of resources that are stored in a specific namespace.
type Namespaced interface {
GetNamespaceUID() string
}
// AlertRuleWithOptionals This is to avoid having to pass in additional arguments deep in the call stack. Alert rule
// object is created in an early validation step without knowledge about current alert rule fields or if they need to be
// overridden. This is done in a later step and, in that step, we did not have knowledge about if a field was optional
+1 -2
View File
@@ -9,7 +9,6 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
"github.com/grafana/grafana/pkg/services/ngalert/models"
)
@@ -24,7 +23,7 @@ type SilenceService struct {
}
type RuleAccessControlService interface {
HasAccessInFolder(ctx context.Context, user identity.Requester, rule accesscontrol.Namespaced) (bool, error)
HasAccessInFolder(ctx context.Context, user identity.Requester, rule models.Namespaced) (bool, error)
}
// SilenceAccessControlService provides access control for silences.
@@ -15,7 +15,6 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity"
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
"github.com/grafana/grafana/pkg/services/ngalert/accesscontrol/fakes"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/org"
@@ -61,7 +60,7 @@ func TestWithRuleMetadata(t *testing.T) {
user := ac.BackgroundUser("test", 1, org.RoleNone, nil)
t.Run("Attach rule metadata to silences", func(t *testing.T) {
ruleAuthz := fakes.FakeRuleService{}
ruleAuthz.HasAccessInFolderFunc = func(ctx context.Context, user identity.Requester, silence accesscontrol.Namespaced) (bool, error) {
ruleAuthz.HasAccessInFolderFunc = func(ctx context.Context, user identity.Requester, silence models.Namespaced) (bool, error) {
return true, nil
}
@@ -95,7 +94,7 @@ func TestWithRuleMetadata(t *testing.T) {
})
t.Run("Don't attach full rule metadata if no access or global", func(t *testing.T) {
ruleAuthz := fakes.FakeRuleService{}
ruleAuthz.HasAccessInFolderFunc = func(ctx context.Context, user identity.Requester, silence accesscontrol.Namespaced) (bool, error) {
ruleAuthz.HasAccessInFolderFunc = func(ctx context.Context, user identity.Requester, silence models.Namespaced) (bool, error) {
return silence.GetNamespaceUID() == "folder1", nil
}
@@ -134,7 +133,7 @@ func TestWithRuleMetadata(t *testing.T) {
})
t.Run("Don't check same namespace access more than once", func(t *testing.T) {
ruleAuthz := fakes.FakeRuleService{}
ruleAuthz.HasAccessInFolderFunc = func(ctx context.Context, user identity.Requester, silence accesscontrol.Namespaced) (bool, error) {
ruleAuthz.HasAccessInFolderFunc = func(ctx context.Context, user identity.Requester, silence models.Namespaced) (bool, error) {
return true, nil
}
@@ -159,7 +158,7 @@ func TestWithRuleMetadata(t *testing.T) {
require.NoError(t, svc.WithRuleMetadata(context.Background(), user, silencesWithMetadata...))
assert.Lenf(t, ruleAuthz.Calls, 1, "HasAccessInFolder should be called only once per namespace")
assert.Equal(t, "HasAccessInFolder", ruleAuthz.Calls[0].MethodName)
assert.Equal(t, "folder1", ruleAuthz.Calls[0].Arguments[2].(accesscontrol.Namespaced).GetNamespaceUID())
assert.Equal(t, "folder1", ruleAuthz.Calls[0].Arguments[2].(models.Namespaced).GetNamespaceUID())
})
}
@@ -5,7 +5,6 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity"
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/store"
)
@@ -13,7 +12,7 @@ import (
type RuleAccessControlService interface {
HasAccess(ctx context.Context, user identity.Requester, evaluator ac.Evaluator) (bool, error)
AuthorizeAccessToRuleGroup(ctx context.Context, user identity.Requester, rules models.RulesGroup) error
AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, namespaced accesscontrol.Namespaced) error
AuthorizeAccessInFolder(ctx context.Context, user identity.Requester, namespaced models.Namespaced) error
AuthorizeRuleChanges(ctx context.Context, user identity.Requester, change *store.GroupDelta) error
}
@@ -11,7 +11,6 @@ import (
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/services/accesscontrol"
accesscontrol2 "github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
"github.com/grafana/grafana/pkg/services/ngalert/accesscontrol/fakes"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/store"
@@ -200,7 +199,7 @@ func TestAuthorizeAccessToRule(t *testing.T) {
rs.HasAccessFunc = func(ctx context.Context, user identity.Requester, evaluator accesscontrol.Evaluator) (bool, error) {
return false, nil
}
rs.AuthorizeAccessInFolderFunc = func(ctx context.Context, requester identity.Requester, namespaced accesscontrol2.Namespaced) error {
rs.AuthorizeAccessInFolderFunc = func(ctx context.Context, requester identity.Requester, namespaced models.Namespaced) error {
return nil
}
@@ -231,7 +230,7 @@ func TestAuthorizeAccessToRule(t *testing.T) {
return false, nil
}
expected = errors.New("test2")
rs.AuthorizeAccessInFolderFunc = func(ctx context.Context, requester identity.Requester, rule accesscontrol2.Namespaced) error {
rs.AuthorizeAccessInFolderFunc = func(ctx context.Context, requester identity.Requester, rule models.Namespaced) error {
return expected
}
@@ -1020,7 +1020,7 @@ func TestGetAlertRule(t *testing.T) {
service, _, _, ac := initServiceWithData(t)
expected := errors.New("test")
ac.AuthorizeAccessInFolderFunc = func(ctx context.Context, user identity.Requester, namespaced accesscontrol.Namespaced) error {
ac.AuthorizeAccessInFolderFunc = func(ctx context.Context, user identity.Requester, namespaced models.Namespaced) error {
assert.Equal(t, u, user)
assert.EqualValues(t, rule, namespaced)
return expected
@@ -1034,7 +1034,7 @@ func TestGetAlertRule(t *testing.T) {
assert.Equal(t, "AuthorizeRuleRead", ac.Calls[0].Method)
ac.Calls = nil
ac.AuthorizeAccessInFolderFunc = func(ctx context.Context, user identity.Requester, namespaced accesscontrol.Namespaced) error {
ac.AuthorizeAccessInFolderFunc = func(ctx context.Context, user identity.Requester, namespaced models.Namespaced) error {
return nil
}
+1 -2
View File
@@ -9,7 +9,6 @@ import (
mock "github.com/stretchr/testify/mock"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/services/ngalert/accesscontrol"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/ngalert/notifier"
"github.com/grafana/grafana/pkg/services/ngalert/store"
@@ -161,7 +160,7 @@ type fakeRuleAccessControlService struct {
mu sync.Mutex
Calls []call
AuthorizeAccessToRuleGroupFunc func(ctx context.Context, user identity.Requester, rules models.RulesGroup) error
AuthorizeAccessInFolderFunc func(ctx context.Context, user identity.Requester, namespaced accesscontrol.Namespaced) error
AuthorizeAccessInFolderFunc func(ctx context.Context, user identity.Requester, namespaced models.Namespaced) error
AuthorizeRuleChangesFunc func(ctx context.Context, user identity.Requester, change *store.GroupDelta) error
CanReadAllRulesFunc func(ctx context.Context, user identity.Requester) (bool, error)
CanWriteAllRulesFunc func(ctx context.Context, user identity.Requester) (bool, error)