The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/api/admin_provisioning_test.go

170 lines
4.9 KiB

package api
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/provisioning"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/assert"
)
type reloadProvisioningTestCase struct {
desc string
url string
expectedCode int
expectedBody string
permissions []*accesscontrol.Permission
exit bool
checkCall func(mock provisioning.ProvisioningServiceMock)
}
func TestAPI_AdminProvisioningReload_AccessControl(t *testing.T) {
tests := []reloadProvisioningTestCase{
{
desc: "should work for dashboards with specific scope",
expectedCode: http.StatusOK,
expectedBody: `{"message":"Dashboards config reloaded"}`,
permissions: []*accesscontrol.Permission{
{
Action: ActionProvisioningReload,
Scope: ScopeProvisionersDashboards,
},
},
url: "/api/admin/provisioning/dashboards/reload",
checkCall: func(mock provisioning.ProvisioningServiceMock) {
assert.Len(t, mock.Calls.ProvisionDashboards, 1)
},
},
{
desc: "should work for dashboards with broader scope",
expectedCode: http.StatusOK,
expectedBody: `{"message":"Dashboards config reloaded"}`,
permissions: []*accesscontrol.Permission{
{
Action: ActionProvisioningReload,
Scope: ScopeProvisionersAll,
},
},
url: "/api/admin/provisioning/dashboards/reload",
checkCall: func(mock provisioning.ProvisioningServiceMock) {
assert.Len(t, mock.Calls.ProvisionDashboards, 1)
},
},
{
desc: "should fail for dashboard with wrong scope",
expectedCode: http.StatusForbidden,
permissions: []*accesscontrol.Permission{
{
Action: ActionProvisioningReload,
Scope: "services:noservice",
},
},
url: "/api/admin/provisioning/dashboards/reload",
exit: true,
},
{
desc: "should fail for dashboard with no permission",
expectedCode: http.StatusForbidden,
url: "/api/admin/provisioning/dashboards/reload",
exit: true,
},
{
desc: "should work for notifications with specific scope",
expectedCode: http.StatusOK,
expectedBody: `{"message":"Notifications config reloaded"}`,
permissions: []*accesscontrol.Permission{
{
Action: ActionProvisioningReload,
Scope: ScopeProvisionersNotifications,
},
},
url: "/api/admin/provisioning/notifications/reload",
checkCall: func(mock provisioning.ProvisioningServiceMock) {
assert.Len(t, mock.Calls.ProvisionNotifications, 1)
},
},
{
desc: "should fail for notifications with no permission",
expectedCode: http.StatusForbidden,
url: "/api/admin/provisioning/notifications/reload",
exit: true,
},
{
desc: "should work for datasources with specific scope",
expectedCode: http.StatusOK,
expectedBody: `{"message":"Datasources config reloaded"}`,
permissions: []*accesscontrol.Permission{
{
Action: ActionProvisioningReload,
Scope: ScopeProvisionersDatasources,
},
},
url: "/api/admin/provisioning/datasources/reload",
checkCall: func(mock provisioning.ProvisioningServiceMock) {
assert.Len(t, mock.Calls.ProvisionDatasources, 1)
},
},
{
desc: "should fail for datasources with no permission",
expectedCode: http.StatusForbidden,
url: "/api/admin/provisioning/datasources/reload",
exit: true,
},
{
desc: "should work for plugins with specific scope",
expectedCode: http.StatusOK,
expectedBody: `{"message":"Plugins config reloaded"}`,
permissions: []*accesscontrol.Permission{
{
Action: ActionProvisioningReload,
Scope: ScopeProvisionersPlugins,
},
},
url: "/api/admin/provisioning/plugins/reload",
checkCall: func(mock provisioning.ProvisioningServiceMock) {
assert.Len(t, mock.Calls.ProvisionPlugins, 1)
},
},
{
desc: "should fail for plugins with no permission",
expectedCode: http.StatusForbidden,
url: "/api/admin/provisioning/plugins/reload",
exit: true,
},
}
cfg := setting.NewCfg()
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
sc, hs := setupAccessControlScenarioContext(t, cfg, test.url, test.permissions)
// Setup the mock
provisioningMock := provisioning.NewProvisioningServiceMock(context.Background())
hs.ProvisioningService = provisioningMock
sc.resp = httptest.NewRecorder()
var err error
sc.req, err = http.NewRequest(http.MethodPost, test.url, nil)
assert.NoError(t, err)
sc.exec()
// Check return code
assert.Equal(t, test.expectedCode, sc.resp.Code)
if test.exit {
return
}
// Check body
assert.Equal(t, test.expectedBody, sc.resp.Body.String())
// Check we actually called the provisioning service
test.checkCall(*provisioningMock)
})
}
}