| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | package plugins | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2022-08-10 21:37:51 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2022-08-10 21:37:51 +08:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2021-11-17 23:43:25 +08:00
										 |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	"testing" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-18 19:08:49 +08:00
										 |  |  | 	"github.com/google/go-cmp/cmp" | 
					
						
							|  |  |  | 	"github.com/google/go-cmp/cmp/cmpopts" | 
					
						
							| 
									
										
										
										
											2023-01-30 16:26:42 +08:00
										 |  |  | 	"github.com/stretchr/testify/assert" | 
					
						
							|  |  |  | 	"github.com/stretchr/testify/require" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-18 19:08:49 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/api/dtos" | 
					
						
							| 
									
										
										
										
											2024-04-04 21:04:47 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/infra/db" | 
					
						
							| 
									
										
										
										
											2024-04-30 19:15:56 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/infra/tracing" | 
					
						
							| 
									
										
										
										
											2024-03-18 19:08:49 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/plugins" | 
					
						
							| 
									
										
										
										
											2022-12-08 00:03:22 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/org/orgimpl" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/quota/quotaimpl" | 
					
						
							| 
									
										
										
										
											2023-02-07 00:50:03 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest" | 
					
						
							| 
									
										
										
										
											2022-06-28 20:32:25 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/user" | 
					
						
							| 
									
										
										
										
											2022-12-08 00:03:22 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/user/userimpl" | 
					
						
							| 
									
										
										
										
											2023-03-07 01:42:18 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/setting" | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/tests/testinfra" | 
					
						
							| 
									
										
										
										
											2024-02-09 22:35:39 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/tests/testsuite" | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	usernameAdmin    = "admin" | 
					
						
							|  |  |  | 	usernameNonAdmin = "nonAdmin" | 
					
						
							|  |  |  | 	defaultPassword  = "password" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-17 23:43:25 +08:00
										 |  |  | var updateSnapshotFlag = false | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-09 22:35:39 +08:00
										 |  |  | func TestMain(m *testing.M) { | 
					
						
							|  |  |  | 	testsuite.Run(m) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-09 15:11:56 +08:00
										 |  |  | func TestIntegrationPlugins(t *testing.T) { | 
					
						
							|  |  |  | 	if testing.Short() { | 
					
						
							|  |  |  | 		t.Skip("skipping integration test") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	dir, cfgPath := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{ | 
					
						
							|  |  |  | 		PluginAdminEnabled: true, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-30 17:48:52 +08:00
										 |  |  | 	origBuildVersion := setting.BuildVersion | 
					
						
							|  |  |  | 	setting.BuildVersion = "0.0.0-test" | 
					
						
							|  |  |  | 	t.Cleanup(func() { | 
					
						
							|  |  |  | 		setting.BuildVersion = origBuildVersion | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-04 21:04:47 +08:00
										 |  |  | 	grafanaListedAddr, env := testinfra.StartGrafanaEnv(t, dir, cfgPath) | 
					
						
							|  |  |  | 	store := env.SQLStore | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	type testCase struct { | 
					
						
							| 
									
										
										
										
											2021-11-17 23:43:25 +08:00
										 |  |  | 		desc        string | 
					
						
							|  |  |  | 		url         string | 
					
						
							|  |  |  | 		expStatus   int | 
					
						
							|  |  |  | 		expRespPath string | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	t.Run("Install", func(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2024-04-04 21:04:47 +08:00
										 |  |  | 		createUser(t, store, env.Cfg, user.CreateUserCommand{Login: usernameNonAdmin, Password: defaultPassword, IsAdmin: false}) | 
					
						
							|  |  |  | 		createUser(t, store, env.Cfg, user.CreateUserCommand{Login: usernameAdmin, Password: defaultPassword, IsAdmin: true}) | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		t.Run("Request is forbidden if not from an admin", func(t *testing.T) { | 
					
						
							|  |  |  | 			status, body := makePostRequest(t, grafanaAPIURL(usernameNonAdmin, grafanaListedAddr, "plugins/grafana-plugin/install")) | 
					
						
							|  |  |  | 			assert.Equal(t, 403, status) | 
					
						
							| 
									
										
										
										
											2022-09-09 15:44:50 +08:00
										 |  |  | 			assert.Equal(t, "You'll need additional permissions to perform this action. Permissions needed: plugins:install", body["message"]) | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			status, body = makePostRequest(t, grafanaAPIURL(usernameNonAdmin, grafanaListedAddr, "plugins/grafana-plugin/uninstall")) | 
					
						
							|  |  |  | 			assert.Equal(t, 403, status) | 
					
						
							| 
									
										
										
										
											2022-09-09 15:44:50 +08:00
										 |  |  | 			assert.Equal(t, "You'll need additional permissions to perform this action. Permissions needed: plugins:install", body["message"]) | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		t.Run("Request is not forbidden if from an admin", func(t *testing.T) { | 
					
						
							|  |  |  | 			statusCode, body := makePostRequest(t, grafanaAPIURL(usernameAdmin, grafanaListedAddr, "plugins/test/install")) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			assert.Equal(t, 404, statusCode) | 
					
						
							|  |  |  | 			assert.Equal(t, "Plugin not found", body["message"]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			statusCode, body = makePostRequest(t, grafanaAPIURL(usernameAdmin, grafanaListedAddr, "plugins/test/uninstall")) | 
					
						
							|  |  |  | 			assert.Equal(t, 404, statusCode) | 
					
						
							|  |  |  | 			assert.Equal(t, "Plugin not installed", body["message"]) | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-25 22:08:18 +08:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE:
 | 
					
						
							|  |  |  | 	// If this test is failing due to changes in plugins just rerun with updateSnapshotFlag = true at the top.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	t.Run("List", func(t *testing.T) { | 
					
						
							|  |  |  | 		testCases := []testCase{ | 
					
						
							|  |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2021-11-17 23:43:25 +08:00
										 |  |  | 				desc:        "should return all loaded core and bundled plugins", | 
					
						
							|  |  |  | 				url:         "http://%s/api/plugins", | 
					
						
							|  |  |  | 				expStatus:   http.StatusOK, | 
					
						
							|  |  |  | 				expRespPath: "expectedListResp.json", | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for _, tc := range testCases { | 
					
						
							|  |  |  | 			t.Run(tc.desc, func(t *testing.T) { | 
					
						
							|  |  |  | 				url := fmt.Sprintf(tc.url, grafanaListedAddr) | 
					
						
							|  |  |  | 				// nolint:gosec
 | 
					
						
							|  |  |  | 				resp, err := http.Get(url) | 
					
						
							|  |  |  | 				t.Cleanup(func() { | 
					
						
							|  |  |  | 					require.NoError(t, resp.Body.Close()) | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 				require.NoError(t, err) | 
					
						
							|  |  |  | 				require.Equal(t, tc.expStatus, resp.StatusCode) | 
					
						
							| 
									
										
										
										
											2022-08-10 21:37:51 +08:00
										 |  |  | 				b, err := io.ReadAll(resp.Body) | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 				require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2024-03-18 19:08:49 +08:00
										 |  |  | 				var result dtos.PluginList | 
					
						
							|  |  |  | 				err = json.Unmarshal(b, &result) | 
					
						
							|  |  |  | 				require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2021-11-17 23:43:25 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				expResp := expectedResp(t, tc.expRespPath) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-18 19:08:49 +08:00
										 |  |  | 				if diff := cmp.Diff(expResp, result, cmpopts.IgnoreFields(plugins.Info{}, "Version")); diff != "" { | 
					
						
							| 
									
										
										
										
											2021-11-17 23:43:25 +08:00
										 |  |  | 					if updateSnapshotFlag { | 
					
						
							|  |  |  | 						t.Log("updating snapshot results") | 
					
						
							| 
									
										
										
										
											2022-06-14 15:35:20 +08:00
										 |  |  | 						var prettyJSON bytes.Buffer | 
					
						
							|  |  |  | 						if err := json.Indent(&prettyJSON, b, "", "  "); err != nil { | 
					
						
							|  |  |  | 							t.FailNow() | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						updateRespSnapshot(t, tc.expRespPath, prettyJSON.String()) | 
					
						
							| 
									
										
										
										
											2021-11-17 23:43:25 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2024-03-18 19:08:49 +08:00
										 |  |  | 					t.Errorf("unexpected response (-want +got):\n%s", diff) | 
					
						
							| 
									
										
										
										
											2021-11-17 23:43:25 +08:00
										 |  |  | 					t.FailNow() | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-07 01:42:18 +08:00
										 |  |  | func TestIntegrationPluginAssets(t *testing.T) { | 
					
						
							|  |  |  | 	if testing.Short() { | 
					
						
							|  |  |  | 		t.Skip("skipping integration test") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	type testCase struct { | 
					
						
							|  |  |  | 		desc            string | 
					
						
							|  |  |  | 		url             string | 
					
						
							|  |  |  | 		env             string | 
					
						
							|  |  |  | 		expStatus       int | 
					
						
							|  |  |  | 		expCacheControl string | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	t.Run("Assets", func(t *testing.T) { | 
					
						
							|  |  |  | 		testCases := []testCase{ | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				desc:            "should return no-cache settings for Dev env", | 
					
						
							|  |  |  | 				env:             setting.Dev, | 
					
						
							| 
									
										
										
										
											2023-09-22 20:00:40 +08:00
										 |  |  | 				url:             "http://%s/public/plugins/grafana-testdata-datasource/img/testdata.svg", | 
					
						
							| 
									
										
										
										
											2023-03-07 01:42:18 +08:00
										 |  |  | 				expStatus:       http.StatusOK, | 
					
						
							|  |  |  | 				expCacheControl: "max-age=0, must-revalidate, no-cache", | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				desc:            "should return cache settings for Prod env", | 
					
						
							|  |  |  | 				env:             setting.Prod, | 
					
						
							| 
									
										
										
										
											2023-09-22 20:00:40 +08:00
										 |  |  | 				url:             "http://%s/public/plugins/grafana-testdata-datasource/img/testdata.svg", | 
					
						
							| 
									
										
										
										
											2023-03-07 01:42:18 +08:00
										 |  |  | 				expStatus:       http.StatusOK, | 
					
						
							|  |  |  | 				expCacheControl: "public, max-age=3600", | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for _, tc := range testCases { | 
					
						
							|  |  |  | 			t.Run(tc.desc, func(t *testing.T) { | 
					
						
							|  |  |  | 				dir, cfgPath := testinfra.CreateGrafDir(t, testinfra.GrafanaOpts{ | 
					
						
							|  |  |  | 					AppModeProduction: tc.env == setting.Prod, | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				grafanaListedAddr, _ := testinfra.StartGrafana(t, dir, cfgPath) | 
					
						
							|  |  |  | 				url := fmt.Sprintf(tc.url, grafanaListedAddr) | 
					
						
							|  |  |  | 				// nolint:gosec
 | 
					
						
							|  |  |  | 				resp, err := http.Get(url) | 
					
						
							|  |  |  | 				t.Cleanup(func() { | 
					
						
							|  |  |  | 					require.NoError(t, resp.Body.Close()) | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 				require.NoError(t, err) | 
					
						
							|  |  |  | 				require.Equal(t, tc.expStatus, resp.StatusCode) | 
					
						
							|  |  |  | 				require.Equal(t, tc.expCacheControl, resp.Header.Get("Cache-Control")) | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-09 01:41:33 +08:00
										 |  |  | func createUser(t *testing.T, store db.DB, cfg *setting.Cfg, cmd user.CreateUserCommand) { | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	t.Helper() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-04 21:04:47 +08:00
										 |  |  | 	cfg.AutoAssignOrg = true | 
					
						
							|  |  |  | 	cfg.AutoAssignOrgId = 1 | 
					
						
							| 
									
										
										
										
											2022-06-07 21:49:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-09 01:41:33 +08:00
										 |  |  | 	quotaService := quotaimpl.ProvideService(db.FakeReplDBFromDB(store), cfg) | 
					
						
							|  |  |  | 	orgService, err := orgimpl.ProvideService(store, cfg, quotaService) | 
					
						
							| 
									
										
										
										
											2022-12-08 00:03:22 +08:00
										 |  |  | 	require.NoError(t, err) | 
					
						
							| 
									
										
										
										
											2024-04-30 19:15:56 +08:00
										 |  |  | 	usrSvc, err := userimpl.ProvideService( | 
					
						
							| 
									
										
										
										
											2024-08-09 01:41:33 +08:00
										 |  |  | 		store, orgService, cfg, nil, nil, tracing.InitializeTracerForTest(), | 
					
						
							| 
									
										
										
										
											2024-04-30 19:15:56 +08:00
										 |  |  | 		quotaService, supportbundlestest.NewFakeBundleService(), | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2022-12-08 00:03:22 +08:00
										 |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-04 00:01:23 +08:00
										 |  |  | 	_, err = usrSvc.Create(context.Background(), &cmd) | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func makePostRequest(t *testing.T, URL string) (int, map[string]interface{}) { | 
					
						
							|  |  |  | 	t.Helper() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// nolint:gosec
 | 
					
						
							|  |  |  | 	resp, err := http.Post(URL, "application/json", bytes.NewBufferString("")) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 	t.Cleanup(func() { | 
					
						
							| 
									
										
										
										
											2023-02-01 22:28:25 +08:00
										 |  |  | 		require.NoError(t, resp.Body.Close()) | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-08-10 21:37:51 +08:00
										 |  |  | 	b, err := io.ReadAll(resp.Body) | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var body = make(map[string]interface{}) | 
					
						
							|  |  |  | 	err = json.Unmarshal(b, &body) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return resp.StatusCode, body | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func grafanaAPIURL(username string, grafanaListedAddr string, path string) string { | 
					
						
							|  |  |  | 	return fmt.Sprintf("http://%s:%s@%s/api/%s", username, defaultPassword, grafanaListedAddr, path) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-18 19:08:49 +08:00
										 |  |  | func expectedResp(t *testing.T, filename string) dtos.PluginList { | 
					
						
							| 
									
										
										
										
											2021-11-17 23:43:25 +08:00
										 |  |  | 	//nolint:GOSEC
 | 
					
						
							| 
									
										
										
										
											2022-08-10 21:37:51 +08:00
										 |  |  | 	contents, err := os.ReadFile(filepath.Join("data", filename)) | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Errorf("failed to load %s: %v", filename, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-18 19:08:49 +08:00
										 |  |  | 	var result dtos.PluginList | 
					
						
							|  |  |  | 	err = json.Unmarshal(contents, &result) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Errorf("failed to unmarshal %s: %v", filename, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return result | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-11-17 23:43:25 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func updateRespSnapshot(t *testing.T, filename string, body string) { | 
					
						
							| 
									
										
										
										
											2022-08-10 21:37:51 +08:00
										 |  |  | 	err := os.WriteFile(filepath.Join("data", filename), []byte(body), 0600) | 
					
						
							| 
									
										
										
										
											2021-11-17 23:43:25 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Errorf("error writing snapshot %s: %v", filename, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |