| 
									
										
										
										
											2017-02-08 05:15:52 +08:00
										 |  |  | package api | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2021-09-01 21:18:17 +08:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2021-12-01 22:43:31 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2017-02-08 05:15:52 +08:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2021-09-01 21:18:17 +08:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"net/http/httptest" | 
					
						
							| 
									
										
										
										
											2017-02-08 05:15:52 +08:00
										 |  |  | 	"testing" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-15 21:43:20 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/api/response" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/api/routing" | 
					
						
							| 
									
										
										
										
											2022-01-22 07:22:43 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/bus" | 
					
						
							| 
									
										
										
										
											2017-02-08 18:57:05 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/models" | 
					
						
							| 
									
										
										
										
											2021-09-01 21:18:17 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/accesscontrol" | 
					
						
							| 
									
										
										
										
											2022-02-03 16:20:20 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/sqlstore/mockstore" | 
					
						
							| 
									
										
										
										
											2020-12-28 19:24:42 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/setting" | 
					
						
							| 
									
										
										
										
											2020-05-12 19:04:18 +08:00
										 |  |  | 	"github.com/stretchr/testify/assert" | 
					
						
							| 
									
										
										
										
											2020-11-13 16:52:38 +08:00
										 |  |  | 	"github.com/stretchr/testify/require" | 
					
						
							| 
									
										
										
										
											2017-02-08 05:15:52 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2020-11-24 19:10:32 +08:00
										 |  |  | 	testOrgID     int64  = 1 | 
					
						
							|  |  |  | 	testUserID    int64  = 1 | 
					
						
							|  |  |  | 	testUserLogin string = "testUser" | 
					
						
							| 
									
										
										
										
											2017-02-08 05:15:52 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-13 16:52:38 +08:00
										 |  |  | func TestDataSourcesProxy_userLoggedIn(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-02-03 16:20:20 +08:00
										 |  |  | 	mock := mockstore.NewSQLStoreMock() | 
					
						
							| 
									
										
										
										
											2022-01-15 00:55:57 +08:00
										 |  |  | 	loggedInUserScenario(t, "When calling GET on", "/api/datasources/", "/api/datasources/", func(sc *scenarioContext) { | 
					
						
							| 
									
										
										
										
											2020-11-13 16:52:38 +08:00
										 |  |  | 		// Stubs the database query
 | 
					
						
							| 
									
										
										
										
											2021-12-28 23:08:07 +08:00
										 |  |  | 		bus.AddHandler("test", func(ctx context.Context, query *models.GetDataSourcesQuery) error { | 
					
						
							| 
									
										
										
										
											2020-11-13 16:52:38 +08:00
										 |  |  | 			assert.Equal(t, testOrgID, query.OrgId) | 
					
						
							|  |  |  | 			query.Result = []*models.DataSource{ | 
					
						
							|  |  |  | 				{Name: "mmm"}, | 
					
						
							|  |  |  | 				{Name: "ZZZ"}, | 
					
						
							|  |  |  | 				{Name: "BBB"}, | 
					
						
							|  |  |  | 				{Name: "aaa"}, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return nil | 
					
						
							| 
									
										
										
										
											2017-02-08 05:15:52 +08:00
										 |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2018-06-23 10:15:36 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-13 16:52:38 +08:00
										 |  |  | 		// handler func being tested
 | 
					
						
							| 
									
										
										
										
											2021-03-17 23:06:10 +08:00
										 |  |  | 		hs := &HTTPServer{ | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 			Bus:         bus.GetBus(), | 
					
						
							|  |  |  | 			Cfg:         setting.NewCfg(), | 
					
						
							|  |  |  | 			pluginStore: &fakePluginStore{}, | 
					
						
							| 
									
										
										
										
											2022-02-03 16:20:20 +08:00
										 |  |  | 			SQLStore:    mock, | 
					
						
							| 
									
										
										
										
											2021-03-17 23:06:10 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-12-28 19:24:42 +08:00
										 |  |  | 		sc.handlerFunc = hs.GetDataSources | 
					
						
							| 
									
										
										
										
											2020-11-13 16:52:38 +08:00
										 |  |  | 		sc.fakeReq("GET", "/api/datasources").exec() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		respJSON := []map[string]interface{}{} | 
					
						
							|  |  |  | 		err := json.NewDecoder(sc.resp.Body).Decode(&respJSON) | 
					
						
							|  |  |  | 		require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		assert.Equal(t, "aaa", respJSON[0]["name"]) | 
					
						
							|  |  |  | 		assert.Equal(t, "BBB", respJSON[1]["name"]) | 
					
						
							|  |  |  | 		assert.Equal(t, "mmm", respJSON[2]["name"]) | 
					
						
							|  |  |  | 		assert.Equal(t, "ZZZ", respJSON[3]["name"]) | 
					
						
							| 
									
										
										
										
											2022-02-03 16:20:20 +08:00
										 |  |  | 	}, mock) | 
					
						
							| 
									
										
										
										
											2020-11-13 16:52:38 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	loggedInUserScenario(t, "Should be able to save a data source when calling DELETE on non-existing", | 
					
						
							| 
									
										
										
										
											2022-01-15 00:55:57 +08:00
										 |  |  | 		"/api/datasources/name/12345", "/api/datasources/name/:name", func(sc *scenarioContext) { | 
					
						
							| 
									
										
										
										
											2021-05-19 02:39:56 +08:00
										 |  |  | 			// handler func being tested
 | 
					
						
							|  |  |  | 			hs := &HTTPServer{ | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 				Bus:         bus.GetBus(), | 
					
						
							|  |  |  | 				Cfg:         setting.NewCfg(), | 
					
						
							|  |  |  | 				pluginStore: &fakePluginStore{}, | 
					
						
							| 
									
										
										
										
											2021-05-19 02:39:56 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			sc.handlerFunc = hs.DeleteDataSourceByName | 
					
						
							| 
									
										
										
										
											2020-11-13 16:52:38 +08:00
										 |  |  | 			sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec() | 
					
						
							|  |  |  | 			assert.Equal(t, 404, sc.resp.Code) | 
					
						
							| 
									
										
										
										
											2022-02-03 16:20:20 +08:00
										 |  |  | 		}, mock) | 
					
						
							| 
									
										
										
										
											2017-02-08 05:15:52 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-05-12 19:04:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Adding data sources with invalid URLs should lead to an error.
 | 
					
						
							|  |  |  | func TestAddDataSource_InvalidURL(t *testing.T) { | 
					
						
							|  |  |  | 	defer bus.ClearBusHandlers() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-13 16:52:38 +08:00
										 |  |  | 	sc := setupScenarioContext(t, "/api/datasources") | 
					
						
							| 
									
										
										
										
											2020-05-12 19:04:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-15 21:43:20 +08:00
										 |  |  | 	sc.m.Post(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response { | 
					
						
							| 
									
										
										
										
											2021-11-29 17:18:01 +08:00
										 |  |  | 		c.Req.Body = mockRequestBody(models.AddDataSourceCommand{ | 
					
						
							|  |  |  | 			Name:   "Test", | 
					
						
							|  |  |  | 			Url:    "invalid:url", | 
					
						
							|  |  |  | 			Access: "direct", | 
					
						
							|  |  |  | 			Type:   "test", | 
					
						
							| 
									
										
										
										
											2020-05-12 19:04:18 +08:00
										 |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2021-11-29 17:18:01 +08:00
										 |  |  | 		return AddDataSource(c) | 
					
						
							| 
									
										
										
										
											2020-05-12 19:04:18 +08:00
										 |  |  | 	})) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	assert.Equal(t, 400, sc.resp.Code) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Adding data sources with URLs not specifying protocol should work.
 | 
					
						
							|  |  |  | func TestAddDataSource_URLWithoutProtocol(t *testing.T) { | 
					
						
							|  |  |  | 	defer bus.ClearBusHandlers() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const name = "Test" | 
					
						
							|  |  |  | 	const url = "localhost:5432" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Stub handler
 | 
					
						
							| 
									
										
										
										
											2021-12-28 23:08:07 +08:00
										 |  |  | 	bus.AddHandler("sql", func(ctx context.Context, cmd *models.AddDataSourceCommand) error { | 
					
						
							| 
									
										
										
										
											2020-05-12 19:04:18 +08:00
										 |  |  | 		assert.Equal(t, name, cmd.Name) | 
					
						
							|  |  |  | 		assert.Equal(t, url, cmd.Url) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		cmd.Result = &models.DataSource{} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-13 16:52:38 +08:00
										 |  |  | 	sc := setupScenarioContext(t, "/api/datasources") | 
					
						
							| 
									
										
										
										
											2020-05-12 19:04:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-15 21:43:20 +08:00
										 |  |  | 	sc.m.Post(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response { | 
					
						
							| 
									
										
										
										
											2021-11-29 17:18:01 +08:00
										 |  |  | 		c.Req.Body = mockRequestBody(models.AddDataSourceCommand{ | 
					
						
							|  |  |  | 			Name:   name, | 
					
						
							|  |  |  | 			Url:    url, | 
					
						
							|  |  |  | 			Access: "direct", | 
					
						
							|  |  |  | 			Type:   "test", | 
					
						
							| 
									
										
										
										
											2020-05-12 19:04:18 +08:00
										 |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2021-11-29 17:18:01 +08:00
										 |  |  | 		return AddDataSource(c) | 
					
						
							| 
									
										
										
										
											2020-05-12 19:04:18 +08:00
										 |  |  | 	})) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	assert.Equal(t, 200, sc.resp.Code) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Updating data sources with invalid URLs should lead to an error.
 | 
					
						
							|  |  |  | func TestUpdateDataSource_InvalidURL(t *testing.T) { | 
					
						
							|  |  |  | 	defer bus.ClearBusHandlers() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-13 16:52:38 +08:00
										 |  |  | 	sc := setupScenarioContext(t, "/api/datasources/1234") | 
					
						
							| 
									
										
										
										
											2020-05-12 19:04:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-15 21:43:20 +08:00
										 |  |  | 	sc.m.Put(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response { | 
					
						
							| 
									
										
										
										
											2021-11-29 17:18:01 +08:00
										 |  |  | 		c.Req.Body = mockRequestBody(models.AddDataSourceCommand{ | 
					
						
							|  |  |  | 			Name:   "Test", | 
					
						
							|  |  |  | 			Url:    "invalid:url", | 
					
						
							|  |  |  | 			Access: "direct", | 
					
						
							|  |  |  | 			Type:   "test", | 
					
						
							| 
									
										
										
										
											2020-05-12 19:04:18 +08:00
										 |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2021-11-29 17:18:01 +08:00
										 |  |  | 		return AddDataSource(c) | 
					
						
							| 
									
										
										
										
											2020-05-12 19:04:18 +08:00
										 |  |  | 	})) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	assert.Equal(t, 400, sc.resp.Code) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Updating data sources with URLs not specifying protocol should work.
 | 
					
						
							|  |  |  | func TestUpdateDataSource_URLWithoutProtocol(t *testing.T) { | 
					
						
							|  |  |  | 	defer bus.ClearBusHandlers() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const name = "Test" | 
					
						
							|  |  |  | 	const url = "localhost:5432" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Stub handler
 | 
					
						
							| 
									
										
										
										
											2021-12-28 23:08:07 +08:00
										 |  |  | 	bus.AddHandler("sql", func(ctx context.Context, cmd *models.AddDataSourceCommand) error { | 
					
						
							| 
									
										
										
										
											2020-05-12 19:04:18 +08:00
										 |  |  | 		assert.Equal(t, name, cmd.Name) | 
					
						
							|  |  |  | 		assert.Equal(t, url, cmd.Url) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		cmd.Result = &models.DataSource{} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-13 16:52:38 +08:00
										 |  |  | 	sc := setupScenarioContext(t, "/api/datasources/1234") | 
					
						
							| 
									
										
										
										
											2020-05-12 19:04:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-15 21:43:20 +08:00
										 |  |  | 	sc.m.Put(sc.url, routing.Wrap(func(c *models.ReqContext) response.Response { | 
					
						
							| 
									
										
										
										
											2021-11-29 17:18:01 +08:00
										 |  |  | 		c.Req.Body = mockRequestBody(models.AddDataSourceCommand{ | 
					
						
							|  |  |  | 			Name:   name, | 
					
						
							|  |  |  | 			Url:    url, | 
					
						
							|  |  |  | 			Access: "direct", | 
					
						
							|  |  |  | 			Type:   "test", | 
					
						
							| 
									
										
										
										
											2020-05-12 19:04:18 +08:00
										 |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2021-11-29 17:18:01 +08:00
										 |  |  | 		return AddDataSource(c) | 
					
						
							| 
									
										
										
										
											2020-05-12 19:04:18 +08:00
										 |  |  | 	})) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	assert.Equal(t, 200, sc.resp.Code) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-09-01 21:18:17 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func TestAPI_Datasources_AccessControl(t *testing.T) { | 
					
						
							|  |  |  | 	testDatasource := models.DataSource{ | 
					
						
							|  |  |  | 		Id:     3, | 
					
						
							|  |  |  | 		Uid:    "testUID", | 
					
						
							|  |  |  | 		OrgId:  testOrgID, | 
					
						
							|  |  |  | 		Name:   "test", | 
					
						
							|  |  |  | 		Url:    "http://localhost:5432", | 
					
						
							|  |  |  | 		Type:   "postgresql", | 
					
						
							|  |  |  | 		Access: "Proxy", | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-01-22 07:22:43 +08:00
										 |  |  | 	testDatasourceReadOnly := models.DataSource{ | 
					
						
							|  |  |  | 		Id:       4, | 
					
						
							|  |  |  | 		Uid:      "testUID", | 
					
						
							|  |  |  | 		OrgId:    testOrgID, | 
					
						
							|  |  |  | 		Name:     "test", | 
					
						
							|  |  |  | 		Url:      "http://localhost:5432", | 
					
						
							|  |  |  | 		Type:     "postgresql", | 
					
						
							|  |  |  | 		Access:   "Proxy", | 
					
						
							|  |  |  | 		ReadOnly: true, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-01 22:43:31 +08:00
										 |  |  | 	getDatasourceStub := func(ctx context.Context, query *models.GetDataSourceQuery) error { | 
					
						
							| 
									
										
										
										
											2021-09-01 21:18:17 +08:00
										 |  |  | 		result := testDatasource | 
					
						
							|  |  |  | 		result.Id = query.Id | 
					
						
							|  |  |  | 		result.OrgId = query.OrgId | 
					
						
							|  |  |  | 		query.Result = &result | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-01 22:43:31 +08:00
										 |  |  | 	getDatasourcesStub := func(ctx context.Context, cmd *models.GetDataSourcesQuery) error { | 
					
						
							| 
									
										
										
										
											2021-09-01 21:18:17 +08:00
										 |  |  | 		cmd.Result = []*models.DataSource{} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-01 22:43:31 +08:00
										 |  |  | 	addDatasourceStub := func(ctx context.Context, cmd *models.AddDataSourceCommand) error { | 
					
						
							| 
									
										
										
										
											2021-09-01 21:18:17 +08:00
										 |  |  | 		cmd.Result = &testDatasource | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-12-01 22:43:31 +08:00
										 |  |  | 	updateDatasourceStub := func(ctx context.Context, cmd *models.UpdateDataSourceCommand) error { | 
					
						
							| 
									
										
										
										
											2021-09-01 21:18:17 +08:00
										 |  |  | 		cmd.Result = &testDatasource | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-01-22 07:22:43 +08:00
										 |  |  | 	updateDatasourceReadOnlyStub := func(ctx context.Context, cmd *models.UpdateDataSourceCommand) error { | 
					
						
							|  |  |  | 		cmd.Result = &testDatasourceReadOnly | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	getDatasourceNotFoundStub := func(ctx context.Context, cmd *models.GetDataSourceQuery) error { | 
					
						
							|  |  |  | 		cmd.Result = nil | 
					
						
							|  |  |  | 		return models.ErrDataSourceNotFound | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	getDatasourceReadOnlyStub := func(ctx context.Context, query *models.GetDataSourceQuery) error { | 
					
						
							|  |  |  | 		query.Result = &testDatasourceReadOnly | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-01 22:43:31 +08:00
										 |  |  | 	deleteDatasourceStub := func(ctx context.Context, cmd *models.DeleteDataSourceCommand) error { | 
					
						
							| 
									
										
										
										
											2021-09-01 21:18:17 +08:00
										 |  |  | 		cmd.DeletedDatasourcesCount = 1 | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	addDatasourceBody := func() io.Reader { | 
					
						
							|  |  |  | 		s, _ := json.Marshal(models.AddDataSourceCommand{ | 
					
						
							|  |  |  | 			Name:   "test", | 
					
						
							|  |  |  | 			Url:    "http://localhost:5432", | 
					
						
							|  |  |  | 			Type:   "postgresql", | 
					
						
							|  |  |  | 			Access: "Proxy", | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		return bytes.NewReader(s) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	updateDatasourceBody := func() io.Reader { | 
					
						
							|  |  |  | 		s, _ := json.Marshal(models.UpdateDataSourceCommand{ | 
					
						
							|  |  |  | 			Name:   "test", | 
					
						
							|  |  |  | 			Url:    "http://localhost:5432", | 
					
						
							|  |  |  | 			Type:   "postgresql", | 
					
						
							|  |  |  | 			Access: "Proxy", | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		return bytes.NewReader(s) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	type acTestCaseWithHandler struct { | 
					
						
							|  |  |  | 		busStubs []bus.HandlerFunc | 
					
						
							|  |  |  | 		body     func() io.Reader | 
					
						
							|  |  |  | 		accessControlTestCase | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	tests := []acTestCaseWithHandler{ | 
					
						
							| 
									
										
										
										
											2022-01-22 07:22:43 +08:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			busStubs: []bus.HandlerFunc{getDatasourceNotFoundStub, updateDatasourceStub}, | 
					
						
							|  |  |  | 			body:     updateDatasourceBody, | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusNotFound, | 
					
						
							|  |  |  | 				desc:         "DatasourcesPut should return 404 if datasource not found", | 
					
						
							|  |  |  | 				url:          fmt.Sprintf("/api/datasources/%v", "12345678"), | 
					
						
							|  |  |  | 				method:       http.MethodPut, | 
					
						
							|  |  |  | 				permissions: []*accesscontrol.Permission{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Action: ActionDatasourcesWrite, | 
					
						
							|  |  |  | 						Scope:  ScopeDatasourcesAll, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2021-09-01 21:18:17 +08:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			busStubs: []bus.HandlerFunc{getDatasourcesStub}, | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusOK, | 
					
						
							|  |  |  | 				desc:         "DatasourcesGet should return 200 for user with correct permissions", | 
					
						
							|  |  |  | 				url:          "/api/datasources/", | 
					
						
							|  |  |  | 				method:       http.MethodGet, | 
					
						
							| 
									
										
										
										
											2021-09-07 23:13:11 +08:00
										 |  |  | 				permissions:  []*accesscontrol.Permission{{Action: ActionDatasourcesRead, Scope: ScopeDatasourcesAll}}, | 
					
						
							| 
									
										
										
										
											2021-09-01 21:18:17 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusForbidden, | 
					
						
							|  |  |  | 				desc:         "DatasourcesGet should return 403 for user without required permissions", | 
					
						
							|  |  |  | 				url:          "/api/datasources/", | 
					
						
							|  |  |  | 				method:       http.MethodGet, | 
					
						
							|  |  |  | 				permissions:  []*accesscontrol.Permission{{Action: "wrong"}}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			busStubs: []bus.HandlerFunc{addDatasourceStub}, | 
					
						
							|  |  |  | 			body:     addDatasourceBody, | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusOK, | 
					
						
							|  |  |  | 				desc:         "DatasourcesPost should return 200 for user with correct permissions", | 
					
						
							|  |  |  | 				url:          "/api/datasources/", | 
					
						
							|  |  |  | 				method:       http.MethodPost, | 
					
						
							|  |  |  | 				permissions:  []*accesscontrol.Permission{{Action: ActionDatasourcesCreate}}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusForbidden, | 
					
						
							|  |  |  | 				desc:         "DatasourcesPost should return 403 for user without required permissions", | 
					
						
							|  |  |  | 				url:          "/api/datasources/", | 
					
						
							|  |  |  | 				method:       http.MethodPost, | 
					
						
							|  |  |  | 				permissions:  []*accesscontrol.Permission{{Action: "wrong"}}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			busStubs: []bus.HandlerFunc{getDatasourceStub, updateDatasourceStub}, | 
					
						
							|  |  |  | 			body:     updateDatasourceBody, | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusOK, | 
					
						
							|  |  |  | 				desc:         "DatasourcesPut should return 200 for user with correct permissions", | 
					
						
							|  |  |  | 				url:          fmt.Sprintf("/api/datasources/%v", testDatasource.Id), | 
					
						
							|  |  |  | 				method:       http.MethodPut, | 
					
						
							|  |  |  | 				permissions: []*accesscontrol.Permission{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Action: ActionDatasourcesWrite, | 
					
						
							|  |  |  | 						Scope:  fmt.Sprintf("datasources:id:%v", testDatasource.Id), | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusForbidden, | 
					
						
							|  |  |  | 				desc:         "DatasourcesPut should return 403 for user without required permissions", | 
					
						
							|  |  |  | 				url:          fmt.Sprintf("/api/datasources/%v", testDatasource.Id), | 
					
						
							|  |  |  | 				method:       http.MethodPut, | 
					
						
							|  |  |  | 				permissions:  []*accesscontrol.Permission{{Action: "wrong"}}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2022-01-22 07:22:43 +08:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			busStubs: []bus.HandlerFunc{getDatasourceReadOnlyStub, updateDatasourceReadOnlyStub}, | 
					
						
							|  |  |  | 			body:     updateDatasourceBody, | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusForbidden, | 
					
						
							|  |  |  | 				desc:         "DatasourcesPut should return 403 for read only datasource", | 
					
						
							|  |  |  | 				url:          fmt.Sprintf("/api/datasources/%v", testDatasourceReadOnly.Id), | 
					
						
							|  |  |  | 				method:       http.MethodPut, | 
					
						
							|  |  |  | 				permissions: []*accesscontrol.Permission{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Action: ActionDatasourcesWrite, | 
					
						
							|  |  |  | 						Scope:  fmt.Sprintf("datasources:id:%v", testDatasourceReadOnly.Id), | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2021-09-01 21:18:17 +08:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			busStubs: []bus.HandlerFunc{getDatasourceStub, deleteDatasourceStub}, | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusOK, | 
					
						
							|  |  |  | 				desc:         "DatasourcesDeleteByID should return 200 for user with correct permissions", | 
					
						
							|  |  |  | 				url:          fmt.Sprintf("/api/datasources/%v", testDatasource.Id), | 
					
						
							|  |  |  | 				method:       http.MethodDelete, | 
					
						
							|  |  |  | 				permissions: []*accesscontrol.Permission{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Action: ActionDatasourcesDelete, | 
					
						
							|  |  |  | 						Scope:  fmt.Sprintf("datasources:id:%v", testDatasource.Id), | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusForbidden, | 
					
						
							|  |  |  | 				desc:         "DatasourcesDeleteByID should return 403 for user without required permissions", | 
					
						
							|  |  |  | 				url:          fmt.Sprintf("/api/datasources/%v", testDatasource.Id), | 
					
						
							|  |  |  | 				method:       http.MethodDelete, | 
					
						
							|  |  |  | 				permissions:  []*accesscontrol.Permission{{Action: "wrong"}}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			busStubs: []bus.HandlerFunc{getDatasourceStub, deleteDatasourceStub}, | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusOK, | 
					
						
							|  |  |  | 				desc:         "DatasourcesDeleteByUID should return 200 for user with correct permissions", | 
					
						
							|  |  |  | 				url:          fmt.Sprintf("/api/datasources/uid/%v", testDatasource.Uid), | 
					
						
							|  |  |  | 				method:       http.MethodDelete, | 
					
						
							|  |  |  | 				permissions: []*accesscontrol.Permission{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Action: ActionDatasourcesDelete, | 
					
						
							|  |  |  | 						Scope:  fmt.Sprintf("datasources:uid:%v", testDatasource.Uid), | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusForbidden, | 
					
						
							|  |  |  | 				desc:         "DatasourcesDeleteByUID should return 403 for user without required permissions", | 
					
						
							|  |  |  | 				url:          fmt.Sprintf("/api/datasources/uid/%v", testDatasource.Uid), | 
					
						
							|  |  |  | 				method:       http.MethodDelete, | 
					
						
							|  |  |  | 				permissions:  []*accesscontrol.Permission{{Action: "wrong"}}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			busStubs: []bus.HandlerFunc{getDatasourceStub, deleteDatasourceStub}, | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusOK, | 
					
						
							|  |  |  | 				desc:         "DatasourcesDeleteByName should return 200 for user with correct permissions", | 
					
						
							|  |  |  | 				url:          fmt.Sprintf("/api/datasources/name/%v", testDatasource.Name), | 
					
						
							|  |  |  | 				method:       http.MethodDelete, | 
					
						
							|  |  |  | 				permissions: []*accesscontrol.Permission{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Action: ActionDatasourcesDelete, | 
					
						
							|  |  |  | 						Scope:  fmt.Sprintf("datasources:name:%v", testDatasource.Name), | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusForbidden, | 
					
						
							|  |  |  | 				desc:         "DatasourcesDeleteByName should return 403 for user without required permissions", | 
					
						
							|  |  |  | 				url:          fmt.Sprintf("/api/datasources/name/%v", testDatasource.Name), | 
					
						
							|  |  |  | 				method:       http.MethodDelete, | 
					
						
							|  |  |  | 				permissions:  []*accesscontrol.Permission{{Action: "wrong"}}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			busStubs: []bus.HandlerFunc{getDatasourceStub}, | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusOK, | 
					
						
							|  |  |  | 				desc:         "DatasourcesGetByID should return 200 for user with correct permissions", | 
					
						
							|  |  |  | 				url:          fmt.Sprintf("/api/datasources/%v", testDatasource.Id), | 
					
						
							|  |  |  | 				method:       http.MethodGet, | 
					
						
							|  |  |  | 				permissions: []*accesscontrol.Permission{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Action: ActionDatasourcesRead, | 
					
						
							|  |  |  | 						Scope:  fmt.Sprintf("datasources:id:%v", testDatasource.Id), | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusForbidden, | 
					
						
							|  |  |  | 				desc:         "DatasourcesGetByID should return 403 for user without required permissions", | 
					
						
							|  |  |  | 				url:          fmt.Sprintf("/api/datasources/%v", testDatasource.Id), | 
					
						
							|  |  |  | 				method:       http.MethodGet, | 
					
						
							|  |  |  | 				permissions:  []*accesscontrol.Permission{{Action: "wrong"}}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			busStubs: []bus.HandlerFunc{getDatasourceStub}, | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusOK, | 
					
						
							|  |  |  | 				desc:         "DatasourcesGetByUID should return 200 for user with correct permissions", | 
					
						
							|  |  |  | 				url:          fmt.Sprintf("/api/datasources/uid/%v", testDatasource.Uid), | 
					
						
							|  |  |  | 				method:       http.MethodGet, | 
					
						
							|  |  |  | 				permissions: []*accesscontrol.Permission{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Action: ActionDatasourcesRead, | 
					
						
							|  |  |  | 						Scope:  fmt.Sprintf("datasources:uid:%v", testDatasource.Uid), | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusForbidden, | 
					
						
							|  |  |  | 				desc:         "DatasourcesGetByUID should return 403 for user without required permissions", | 
					
						
							|  |  |  | 				url:          fmt.Sprintf("/api/datasources/uid/%v", testDatasource.Uid), | 
					
						
							|  |  |  | 				method:       http.MethodGet, | 
					
						
							|  |  |  | 				permissions:  []*accesscontrol.Permission{{Action: "wrong"}}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			busStubs: []bus.HandlerFunc{getDatasourceStub}, | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusOK, | 
					
						
							|  |  |  | 				desc:         "DatasourcesGetByName should return 200 for user with correct permissions", | 
					
						
							|  |  |  | 				url:          fmt.Sprintf("/api/datasources/name/%v", testDatasource.Name), | 
					
						
							|  |  |  | 				method:       http.MethodGet, | 
					
						
							|  |  |  | 				permissions: []*accesscontrol.Permission{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Action: ActionDatasourcesRead, | 
					
						
							|  |  |  | 						Scope:  fmt.Sprintf("datasources:name:%v", testDatasource.Name), | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusForbidden, | 
					
						
							|  |  |  | 				desc:         "DatasourcesGetByName should return 403 for user without required permissions", | 
					
						
							|  |  |  | 				url:          fmt.Sprintf("/api/datasources/name/%v", testDatasource.Name), | 
					
						
							|  |  |  | 				method:       http.MethodGet, | 
					
						
							|  |  |  | 				permissions:  []*accesscontrol.Permission{{Action: "wrong"}}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			busStubs: []bus.HandlerFunc{getDatasourceStub}, | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusOK, | 
					
						
							|  |  |  | 				desc:         "DatasourcesGetIdByName should return 200 for user with correct permissions", | 
					
						
							|  |  |  | 				url:          fmt.Sprintf("/api/datasources/id/%v", testDatasource.Name), | 
					
						
							|  |  |  | 				method:       http.MethodGet, | 
					
						
							|  |  |  | 				permissions: []*accesscontrol.Permission{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						Action: ActionDatasourcesIDRead, | 
					
						
							|  |  |  | 						Scope:  fmt.Sprintf("datasources:name:%v", testDatasource.Name), | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			accessControlTestCase: accessControlTestCase{ | 
					
						
							|  |  |  | 				expectedCode: http.StatusForbidden, | 
					
						
							|  |  |  | 				desc:         "DatasourcesGetIdByName should return 403 for user without required permissions", | 
					
						
							|  |  |  | 				url:          fmt.Sprintf("/api/datasources/id/%v", testDatasource.Name), | 
					
						
							|  |  |  | 				method:       http.MethodGet, | 
					
						
							|  |  |  | 				permissions:  []*accesscontrol.Permission{{Action: "wrong"}}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, test := range tests { | 
					
						
							|  |  |  | 		t.Run(test.desc, func(t *testing.T) { | 
					
						
							|  |  |  | 			t.Cleanup(bus.ClearBusHandlers) | 
					
						
							|  |  |  | 			for i, handler := range test.busStubs { | 
					
						
							| 
									
										
										
										
											2021-12-28 23:08:07 +08:00
										 |  |  | 				bus.AddHandler(fmt.Sprintf("test_handler_%v", i), handler) | 
					
						
							| 
									
										
										
										
											2021-09-01 21:18:17 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			cfg := setting.NewCfg() | 
					
						
							|  |  |  | 			sc, hs := setupAccessControlScenarioContext(t, cfg, test.url, test.permissions) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Create a middleware to pretend user is logged in
 | 
					
						
							|  |  |  | 			pretendSignInMiddleware := func(c *models.ReqContext) { | 
					
						
							|  |  |  | 				sc.context = c | 
					
						
							|  |  |  | 				sc.context.UserId = testUserID | 
					
						
							|  |  |  | 				sc.context.OrgId = testOrgID | 
					
						
							|  |  |  | 				sc.context.Login = testUserLogin | 
					
						
							|  |  |  | 				sc.context.OrgRole = models.ROLE_VIEWER | 
					
						
							|  |  |  | 				sc.context.IsSignedIn = true | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			sc.m.Use(pretendSignInMiddleware) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			sc.resp = httptest.NewRecorder() | 
					
						
							|  |  |  | 			hs.SettingsProvider = &setting.OSSImpl{Cfg: cfg} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			var err error | 
					
						
							|  |  |  | 			if test.body != nil { | 
					
						
							|  |  |  | 				sc.req, err = http.NewRequest(test.method, test.url, test.body()) | 
					
						
							|  |  |  | 				sc.req.Header.Add("Content-Type", "application/json") | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				sc.req, err = http.NewRequest(test.method, test.url, nil) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-01-22 07:22:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-01 21:18:17 +08:00
										 |  |  | 			assert.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			sc.exec() | 
					
						
							|  |  |  | 			assert.Equal(t, test.expectedCode, sc.resp.Code) | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |