mirror of
https://github.com/grafana/grafana.git
synced 2024-11-26 02:40:26 -06:00
a367ad730c
* wip: Implement kvstore for secrets
* wip: Refactor kvstore for secrets
* wip: Add format key function to secrets kvstore sql
* wip: Add migration for secrets kvstore
* Remove unused Key field from secrets kvstore
* Remove secret values from debug logs
* Integrate unified secrets with datasources
* Fix minor issues and tests for kvstore
* Create test service helper for secret store
* Remove encryption tests from datasources
* Move secret operations after datasources
* Fix datasource proxy tests
* Fix legacy data tests
* Add Name to all delete data source commands
* Implement decryption cache on sql secret store
* Fix minor issue with cache and tests
* Use secret type on secret store datasource operations
* Add comments to make create and update clear
* Rename itemFound variable to isFound
* Improve secret deletion and cache management
* Add base64 encoding to sql secret store
* Move secret retrieval to decrypted values function
* Refactor decrypt secure json data functions
* Fix expr tests
* Fix datasource tests
* Fix plugin proxy tests
* Fix query tests
* Fix metrics api tests
* Remove unused fake secrets service from query tests
* Add rename function to secret store
* Add check for error renaming secret
* Remove bus from tests to fix merge conflicts
* Add background secrets migration to datasources
* Get datasource secure json fields from secrets
* Move migration to secret store
* Revert "Move migration to secret store"
This reverts commit 7c3f872072
.
* Add secret service to datasource service on tests
* Fix datasource tests
* Remove merge conflict on wire
* Add ctx to data source http transport on prometheus stats collector
* Add ctx to data source http transport on stats collector test
280 lines
8.4 KiB
Go
280 lines
8.4 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
models2 "github.com/grafana/grafana/pkg/models"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
|
acMock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
|
"github.com/grafana/grafana/pkg/services/datasources"
|
|
fakes "github.com/grafana/grafana/pkg/services/datasources/fakes"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
|
"github.com/grafana/grafana/pkg/web"
|
|
)
|
|
|
|
func TestRouteTestGrafanaRuleConfig(t *testing.T) {
|
|
t.Run("when fine-grained access is enabled", func(t *testing.T) {
|
|
rc := &models2.ReqContext{
|
|
Context: &web.Context{
|
|
Req: &http.Request{},
|
|
},
|
|
SignedInUser: &models2.SignedInUser{
|
|
OrgId: 1,
|
|
},
|
|
}
|
|
|
|
t.Run("should return 401 if user cannot query a data source", func(t *testing.T) {
|
|
data1 := models.GenerateAlertQuery()
|
|
data2 := models.GenerateAlertQuery()
|
|
|
|
ac := acMock.New().WithPermissions([]*accesscontrol.Permission{
|
|
{Action: datasources.ActionQuery, Scope: datasources.ScopeProvider.GetResourceScopeUID(data1.DatasourceUID)},
|
|
})
|
|
|
|
srv := createTestingApiSrv(nil, ac, nil)
|
|
|
|
response := srv.RouteTestGrafanaRuleConfig(rc, definitions.TestRulePayload{
|
|
Expr: "",
|
|
GrafanaManagedCondition: &models.EvalAlertConditionCommand{
|
|
Condition: data1.RefID,
|
|
Data: []models.AlertQuery{data1, data2},
|
|
Now: time.Time{},
|
|
},
|
|
})
|
|
|
|
require.Equal(t, http.StatusUnauthorized, response.Status())
|
|
})
|
|
|
|
t.Run("should return 200 if user can query all data sources", func(t *testing.T) {
|
|
data1 := models.GenerateAlertQuery()
|
|
data2 := models.GenerateAlertQuery()
|
|
|
|
ac := acMock.New().WithPermissions([]*accesscontrol.Permission{
|
|
{Action: datasources.ActionQuery, Scope: datasources.ScopeProvider.GetResourceScopeUID(data1.DatasourceUID)},
|
|
{Action: datasources.ActionQuery, Scope: datasources.ScopeProvider.GetResourceScopeUID(data2.DatasourceUID)},
|
|
})
|
|
|
|
ds := &fakes.FakeCacheService{DataSources: []*models2.DataSource{
|
|
{Uid: data1.DatasourceUID},
|
|
{Uid: data2.DatasourceUID},
|
|
}}
|
|
|
|
evaluator := &eval.FakeEvaluator{}
|
|
var result []eval.Result
|
|
evaluator.EXPECT().ConditionEval(mock.Anything, mock.Anything, mock.Anything).Return(result, nil)
|
|
|
|
srv := createTestingApiSrv(ds, ac, evaluator)
|
|
|
|
response := srv.RouteTestGrafanaRuleConfig(rc, definitions.TestRulePayload{
|
|
Expr: "",
|
|
GrafanaManagedCondition: &models.EvalAlertConditionCommand{
|
|
Condition: data1.RefID,
|
|
Data: []models.AlertQuery{data1, data2},
|
|
Now: time.Time{},
|
|
},
|
|
})
|
|
|
|
require.Equal(t, http.StatusOK, response.Status())
|
|
|
|
evaluator.AssertCalled(t, "ConditionEval", mock.Anything, mock.Anything, mock.Anything)
|
|
})
|
|
})
|
|
|
|
t.Run("when fine-grained access is disabled", func(t *testing.T) {
|
|
rc := &models2.ReqContext{
|
|
Context: &web.Context{
|
|
Req: &http.Request{},
|
|
},
|
|
IsSignedIn: false,
|
|
SignedInUser: &models2.SignedInUser{
|
|
OrgId: 1,
|
|
},
|
|
}
|
|
ac := acMock.New().WithDisabled()
|
|
|
|
t.Run("should require user to be signed in", func(t *testing.T) {
|
|
data1 := models.GenerateAlertQuery()
|
|
|
|
ds := &fakes.FakeCacheService{DataSources: []*models2.DataSource{
|
|
{Uid: data1.DatasourceUID},
|
|
}}
|
|
|
|
evaluator := &eval.FakeEvaluator{}
|
|
var result []eval.Result
|
|
evaluator.EXPECT().ConditionEval(mock.Anything, mock.Anything, mock.Anything).Return(result, nil)
|
|
|
|
srv := createTestingApiSrv(ds, ac, evaluator)
|
|
|
|
response := srv.RouteTestGrafanaRuleConfig(rc, definitions.TestRulePayload{
|
|
Expr: "",
|
|
GrafanaManagedCondition: &models.EvalAlertConditionCommand{
|
|
Condition: data1.RefID,
|
|
Data: []models.AlertQuery{data1},
|
|
Now: time.Time{},
|
|
},
|
|
})
|
|
|
|
require.Equal(t, http.StatusUnauthorized, response.Status())
|
|
evaluator.AssertNotCalled(t, "ConditionEval", mock.Anything, mock.Anything, mock.Anything)
|
|
|
|
rc.IsSignedIn = true
|
|
|
|
response = srv.RouteTestGrafanaRuleConfig(rc, definitions.TestRulePayload{
|
|
Expr: "",
|
|
GrafanaManagedCondition: &models.EvalAlertConditionCommand{
|
|
Condition: data1.RefID,
|
|
Data: []models.AlertQuery{data1},
|
|
Now: time.Time{},
|
|
},
|
|
})
|
|
|
|
require.Equal(t, http.StatusOK, response.Status())
|
|
|
|
evaluator.AssertCalled(t, "ConditionEval", mock.Anything, mock.Anything, mock.Anything)
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestRouteEvalQueries(t *testing.T) {
|
|
t.Run("when fine-grained access is enabled", func(t *testing.T) {
|
|
rc := &models2.ReqContext{
|
|
Context: &web.Context{
|
|
Req: &http.Request{},
|
|
},
|
|
SignedInUser: &models2.SignedInUser{
|
|
OrgId: 1,
|
|
},
|
|
}
|
|
|
|
t.Run("should return 401 if user cannot query a data source", func(t *testing.T) {
|
|
data1 := models.GenerateAlertQuery()
|
|
data2 := models.GenerateAlertQuery()
|
|
|
|
ac := acMock.New().WithPermissions([]*accesscontrol.Permission{
|
|
{Action: datasources.ActionQuery, Scope: datasources.ScopeProvider.GetResourceScopeUID(data1.DatasourceUID)},
|
|
})
|
|
|
|
srv := &TestingApiSrv{
|
|
accessControl: ac,
|
|
}
|
|
|
|
response := srv.RouteEvalQueries(rc, definitions.EvalQueriesPayload{
|
|
Data: []models.AlertQuery{data1, data2},
|
|
Now: time.Time{},
|
|
})
|
|
|
|
require.Equal(t, http.StatusUnauthorized, response.Status())
|
|
})
|
|
|
|
t.Run("should return 200 if user can query all data sources", func(t *testing.T) {
|
|
data1 := models.GenerateAlertQuery()
|
|
data2 := models.GenerateAlertQuery()
|
|
|
|
ac := acMock.New().WithPermissions([]*accesscontrol.Permission{
|
|
{Action: datasources.ActionQuery, Scope: datasources.ScopeProvider.GetResourceScopeUID(data1.DatasourceUID)},
|
|
{Action: datasources.ActionQuery, Scope: datasources.ScopeProvider.GetResourceScopeUID(data2.DatasourceUID)},
|
|
})
|
|
|
|
ds := &fakes.FakeCacheService{DataSources: []*models2.DataSource{
|
|
{Uid: data1.DatasourceUID},
|
|
{Uid: data2.DatasourceUID},
|
|
}}
|
|
|
|
evaluator := &eval.FakeEvaluator{}
|
|
result := &backend.QueryDataResponse{
|
|
Responses: map[string]backend.DataResponse{
|
|
"test": {
|
|
Frames: nil,
|
|
Error: nil,
|
|
},
|
|
},
|
|
}
|
|
evaluator.EXPECT().QueriesAndExpressionsEval(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(result, nil)
|
|
|
|
srv := createTestingApiSrv(ds, ac, evaluator)
|
|
|
|
response := srv.RouteEvalQueries(rc, definitions.EvalQueriesPayload{
|
|
Data: []models.AlertQuery{data1, data2},
|
|
Now: time.Time{},
|
|
})
|
|
|
|
require.Equal(t, http.StatusOK, response.Status())
|
|
|
|
evaluator.AssertCalled(t, "QueriesAndExpressionsEval", mock.Anything, mock.Anything, mock.Anything, mock.Anything)
|
|
})
|
|
})
|
|
|
|
t.Run("when fine-grained access is disabled", func(t *testing.T) {
|
|
rc := &models2.ReqContext{
|
|
Context: &web.Context{
|
|
Req: &http.Request{},
|
|
},
|
|
IsSignedIn: false,
|
|
SignedInUser: &models2.SignedInUser{
|
|
OrgId: 1,
|
|
},
|
|
}
|
|
ac := acMock.New().WithDisabled()
|
|
|
|
t.Run("should require user to be signed in", func(t *testing.T) {
|
|
data1 := models.GenerateAlertQuery()
|
|
|
|
ds := &fakes.FakeCacheService{DataSources: []*models2.DataSource{
|
|
{Uid: data1.DatasourceUID},
|
|
}}
|
|
|
|
evaluator := &eval.FakeEvaluator{}
|
|
result := &backend.QueryDataResponse{
|
|
Responses: map[string]backend.DataResponse{
|
|
"test": {
|
|
Frames: nil,
|
|
Error: nil,
|
|
},
|
|
},
|
|
}
|
|
evaluator.EXPECT().QueriesAndExpressionsEval(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(result, nil)
|
|
|
|
srv := createTestingApiSrv(ds, ac, evaluator)
|
|
|
|
response := srv.RouteEvalQueries(rc, definitions.EvalQueriesPayload{
|
|
Data: []models.AlertQuery{data1},
|
|
Now: time.Time{},
|
|
})
|
|
|
|
require.Equal(t, http.StatusUnauthorized, response.Status())
|
|
evaluator.AssertNotCalled(t, "QueriesAndExpressionsEval", mock.Anything, mock.Anything, mock.Anything, mock.Anything)
|
|
|
|
rc.IsSignedIn = true
|
|
|
|
response = srv.RouteEvalQueries(rc, definitions.EvalQueriesPayload{
|
|
Data: []models.AlertQuery{data1},
|
|
Now: time.Time{},
|
|
})
|
|
|
|
require.Equal(t, http.StatusOK, response.Status())
|
|
|
|
evaluator.AssertCalled(t, "QueriesAndExpressionsEval", mock.Anything, mock.Anything, mock.Anything, mock.Anything)
|
|
})
|
|
})
|
|
}
|
|
|
|
func createTestingApiSrv(ds *fakes.FakeCacheService, ac *acMock.Mock, evaluator *eval.FakeEvaluator) *TestingApiSrv {
|
|
if ac == nil {
|
|
ac = acMock.New().WithDisabled()
|
|
}
|
|
|
|
return &TestingApiSrv{
|
|
DatasourceCache: ds,
|
|
accessControl: ac,
|
|
evaluator: evaluator,
|
|
}
|
|
}
|