| 
									
										
										
										
											2021-10-27 19:13:59 +08:00
										 |  |  | package api | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2022-04-29 20:37:33 +08:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2022-08-10 21:37:51 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2021-10-27 19:13:59 +08:00
										 |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-11 21:37:31 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/setting" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-18 02:52:22 +08:00
										 |  |  | 	"github.com/stretchr/testify/assert" | 
					
						
							| 
									
										
										
										
											2022-05-23 23:14:27 +08:00
										 |  |  | 	"github.com/stretchr/testify/mock" | 
					
						
							| 
									
										
										
										
											2022-05-18 02:52:22 +08:00
										 |  |  | 	"github.com/stretchr/testify/require" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-29 20:37:33 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/models" | 
					
						
							| 
									
										
										
										
											2021-10-27 19:13:59 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/accesscontrol" | 
					
						
							| 
									
										
										
										
											2022-05-18 02:52:22 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/dashboards" | 
					
						
							| 
									
										
										
										
											2022-04-21 21:03:17 +08:00
										 |  |  | 	pref "github.com/grafana/grafana/pkg/services/preference" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/preference/preftest" | 
					
						
							| 
									
										
										
										
											2021-10-27 19:13:59 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							| 
									
										
										
										
											2022-03-17 20:07:20 +08:00
										 |  |  | 	getOrgPreferencesURL    = "/api/org/preferences/" | 
					
						
							|  |  |  | 	putOrgPreferencesURL    = "/api/org/preferences/" | 
					
						
							|  |  |  | 	patchOrgPreferencesUrl  = "/api/org/preferences/" | 
					
						
							|  |  |  | 	patchUserPreferencesUrl = "/api/user/preferences/" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-29 20:37:33 +08:00
										 |  |  | 	testUpdateOrgPreferencesCmd                     = `{ "theme": "light", "homeDashboardId": 1 }` | 
					
						
							|  |  |  | 	testPatchOrgPreferencesCmd                      = `{"navbar":{"savedItems":[{"id":"snapshots","text":"Snapshots","icon":"camera","url":"/dashboard/snapshots"}]}}` | 
					
						
							|  |  |  | 	testPatchOrgPreferencesCmdBad                   = `this is not json` | 
					
						
							|  |  |  | 	testPatchUserPreferencesCmd                     = `{"navbar":{"savedItems":[{"id":"snapshots","text":"Snapshots","icon":"camera","url":"/dashboard/snapshots"}]}}` | 
					
						
							|  |  |  | 	testPatchUserPreferencesCmdBad                  = `this is not json` | 
					
						
							|  |  |  | 	testUpdateOrgPreferencesWithHomeDashboardUIDCmd = `{ "theme": "light", "homeDashboardUID": "home"}` | 
					
						
							| 
									
										
										
										
											2021-10-27 19:13:59 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestAPIEndpoint_GetCurrentOrgPreferences_LegacyAccessControl(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-08-11 21:37:31 +08:00
										 |  |  | 	cfg := setting.NewCfg() | 
					
						
							|  |  |  | 	cfg.RBACEnabled = false | 
					
						
							|  |  |  | 	sc := setupHTTPServerWithCfg(t, true, cfg) | 
					
						
							| 
									
										
										
										
											2022-05-23 23:14:27 +08:00
										 |  |  | 	dashSvc := dashboards.NewFakeDashboardService(t) | 
					
						
							|  |  |  | 	dashSvc.On("GetDashboard", mock.Anything, mock.AnythingOfType("*models.GetDashboardQuery")).Run(func(args mock.Arguments) { | 
					
						
							|  |  |  | 		q := args.Get(1).(*models.GetDashboardQuery) | 
					
						
							|  |  |  | 		q.Result = &models.Dashboard{Uid: "home", Id: 1} | 
					
						
							|  |  |  | 	}).Return(nil) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-07 02:42:39 +08:00
										 |  |  | 	sc.hs.DashboardService = dashSvc | 
					
						
							| 
									
										
										
										
											2022-04-29 20:37:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-21 21:03:17 +08:00
										 |  |  | 	prefService := preftest.NewPreferenceServiceFake() | 
					
						
							|  |  |  | 	prefService.ExpectedPreference = &pref.Preference{HomeDashboardID: 1, Theme: "dark"} | 
					
						
							|  |  |  | 	sc.hs.preferenceService = prefService | 
					
						
							| 
									
										
										
										
											2021-10-27 19:13:59 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	_, err := sc.db.CreateOrgWithMember("TestOrg", testUserID) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setInitCtxSignedInViewer(sc.initCtx) | 
					
						
							|  |  |  | 	t.Run("Viewer cannot get org preferences", func(t *testing.T) { | 
					
						
							|  |  |  | 		response := callAPI(sc.server, http.MethodGet, getOrgPreferencesURL, nil, t) | 
					
						
							|  |  |  | 		assert.Equal(t, http.StatusForbidden, response.Code) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setInitCtxSignedInOrgAdmin(sc.initCtx) | 
					
						
							|  |  |  | 	t.Run("Org Admin can get org preferences", func(t *testing.T) { | 
					
						
							|  |  |  | 		response := callAPI(sc.server, http.MethodGet, getOrgPreferencesURL, nil, t) | 
					
						
							|  |  |  | 		assert.Equal(t, http.StatusOK, response.Code) | 
					
						
							| 
									
										
										
										
											2022-04-29 20:37:33 +08:00
										 |  |  | 		var resp map[string]interface{} | 
					
						
							| 
									
										
										
										
											2022-08-10 21:37:51 +08:00
										 |  |  | 		b, err := io.ReadAll(response.Body) | 
					
						
							| 
									
										
										
										
											2022-04-29 20:37:33 +08:00
										 |  |  | 		assert.NoError(t, err) | 
					
						
							|  |  |  | 		assert.NoError(t, json.Unmarshal(b, &resp)) | 
					
						
							|  |  |  | 		assert.Equal(t, "home", resp["homeDashboardUID"]) | 
					
						
							| 
									
										
										
										
											2021-10-27 19:13:59 +08:00
										 |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestAPIEndpoint_GetCurrentOrgPreferences_AccessControl(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-08-11 21:37:31 +08:00
										 |  |  | 	sc := setupHTTPServer(t, true) | 
					
						
							| 
									
										
										
										
											2021-10-27 19:13:59 +08:00
										 |  |  | 	setInitCtxSignedInViewer(sc.initCtx) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-21 21:03:17 +08:00
										 |  |  | 	prefService := preftest.NewPreferenceServiceFake() | 
					
						
							|  |  |  | 	prefService.ExpectedPreference = &pref.Preference{HomeDashboardID: 1, Theme: "dark"} | 
					
						
							|  |  |  | 	sc.hs.preferenceService = prefService | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-27 19:13:59 +08:00
										 |  |  | 	_, err := sc.db.CreateOrgWithMember("TestOrg", testUserID) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	t.Run("AccessControl allows getting org preferences with correct permissions", func(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-08-11 19:28:55 +08:00
										 |  |  | 		setAccessControlPermissions(sc.acmock, []accesscontrol.Permission{{Action: ActionOrgsPreferencesRead}}, sc.initCtx.OrgID) | 
					
						
							| 
									
										
										
										
											2021-10-27 19:13:59 +08:00
										 |  |  | 		response := callAPI(sc.server, http.MethodGet, getOrgPreferencesURL, nil, t) | 
					
						
							|  |  |  | 		assert.Equal(t, http.StatusOK, response.Code) | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2021-11-17 17:12:28 +08:00
										 |  |  | 	t.Run("AccessControl prevents getting org preferences with correct permissions in another org", func(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-06-14 16:17:48 +08:00
										 |  |  | 		setAccessControlPermissions(sc.acmock, []accesscontrol.Permission{{Action: ActionOrgsPreferencesRead}}, 2) | 
					
						
							| 
									
										
										
										
											2021-10-27 19:13:59 +08:00
										 |  |  | 		response := callAPI(sc.server, http.MethodGet, getOrgPreferencesURL, nil, t) | 
					
						
							| 
									
										
										
										
											2021-11-17 17:12:28 +08:00
										 |  |  | 		assert.Equal(t, http.StatusForbidden, response.Code) | 
					
						
							| 
									
										
										
										
											2021-10-27 19:13:59 +08:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 	t.Run("AccessControl prevents getting org preferences with incorrect permissions", func(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-08-11 19:28:55 +08:00
										 |  |  | 		setAccessControlPermissions(sc.acmock, []accesscontrol.Permission{{Action: "orgs:invalid"}}, sc.initCtx.OrgID) | 
					
						
							| 
									
										
										
										
											2021-10-27 19:13:59 +08:00
										 |  |  | 		response := callAPI(sc.server, http.MethodGet, getOrgPreferencesURL, nil, t) | 
					
						
							|  |  |  | 		assert.Equal(t, http.StatusForbidden, response.Code) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestAPIEndpoint_PutCurrentOrgPreferences_LegacyAccessControl(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-08-11 21:37:31 +08:00
										 |  |  | 	cfg := setting.NewCfg() | 
					
						
							|  |  |  | 	cfg.RBACEnabled = false | 
					
						
							|  |  |  | 	sc := setupHTTPServerWithCfg(t, true, cfg) | 
					
						
							| 
									
										
										
										
											2021-10-27 19:13:59 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	_, err := sc.db.CreateOrgWithMember("TestOrg", testUserID) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setInitCtxSignedInViewer(sc.initCtx) | 
					
						
							|  |  |  | 	input := strings.NewReader(testUpdateOrgPreferencesCmd) | 
					
						
							|  |  |  | 	t.Run("Viewer cannot update org preferences", func(t *testing.T) { | 
					
						
							|  |  |  | 		response := callAPI(sc.server, http.MethodPut, putOrgPreferencesURL, input, t) | 
					
						
							|  |  |  | 		assert.Equal(t, http.StatusForbidden, response.Code) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setInitCtxSignedInOrgAdmin(sc.initCtx) | 
					
						
							|  |  |  | 	input = strings.NewReader(testUpdateOrgPreferencesCmd) | 
					
						
							|  |  |  | 	t.Run("Org Admin can update org preferences", func(t *testing.T) { | 
					
						
							|  |  |  | 		response := callAPI(sc.server, http.MethodPut, putOrgPreferencesURL, input, t) | 
					
						
							|  |  |  | 		assert.Equal(t, http.StatusOK, response.Code) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestAPIEndpoint_PutCurrentOrgPreferences_AccessControl(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-08-11 21:37:31 +08:00
										 |  |  | 	sc := setupHTTPServer(t, true) | 
					
						
							| 
									
										
										
										
											2021-10-27 19:13:59 +08:00
										 |  |  | 	setInitCtxSignedInViewer(sc.initCtx) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_, err := sc.db.CreateOrgWithMember("TestOrg", testUserID) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	input := strings.NewReader(testUpdateOrgPreferencesCmd) | 
					
						
							|  |  |  | 	t.Run("AccessControl allows updating org preferences with correct permissions", func(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-08-11 19:28:55 +08:00
										 |  |  | 		setAccessControlPermissions(sc.acmock, []accesscontrol.Permission{{Action: ActionOrgsPreferencesWrite}}, sc.initCtx.OrgID) | 
					
						
							| 
									
										
										
										
											2021-10-27 19:13:59 +08:00
										 |  |  | 		response := callAPI(sc.server, http.MethodPut, putOrgPreferencesURL, input, t) | 
					
						
							|  |  |  | 		assert.Equal(t, http.StatusOK, response.Code) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	input = strings.NewReader(testUpdateOrgPreferencesCmd) | 
					
						
							| 
									
										
										
										
											2021-11-17 17:12:28 +08:00
										 |  |  | 	t.Run("AccessControl prevents updating org preferences with correct permissions in another org", func(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-06-14 16:17:48 +08:00
										 |  |  | 		setAccessControlPermissions(sc.acmock, []accesscontrol.Permission{{Action: ActionOrgsPreferencesWrite}}, 2) | 
					
						
							| 
									
										
										
										
											2021-10-27 19:13:59 +08:00
										 |  |  | 		response := callAPI(sc.server, http.MethodPut, putOrgPreferencesURL, input, t) | 
					
						
							| 
									
										
										
										
											2021-11-17 17:12:28 +08:00
										 |  |  | 		assert.Equal(t, http.StatusForbidden, response.Code) | 
					
						
							| 
									
										
										
										
											2021-10-27 19:13:59 +08:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	input = strings.NewReader(testUpdateOrgPreferencesCmd) | 
					
						
							|  |  |  | 	t.Run("AccessControl prevents updating org preferences with incorrect permissions", func(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-08-11 19:28:55 +08:00
										 |  |  | 		setAccessControlPermissions(sc.acmock, []accesscontrol.Permission{{Action: "orgs:invalid"}}, sc.initCtx.OrgID) | 
					
						
							| 
									
										
										
										
											2021-10-27 19:13:59 +08:00
										 |  |  | 		response := callAPI(sc.server, http.MethodPut, putOrgPreferencesURL, input, t) | 
					
						
							|  |  |  | 		assert.Equal(t, http.StatusForbidden, response.Code) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-03-17 20:07:20 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func TestAPIEndpoint_PatchUserPreferences(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-08-11 21:37:31 +08:00
										 |  |  | 	cfg := setting.NewCfg() | 
					
						
							|  |  |  | 	cfg.RBACEnabled = false | 
					
						
							|  |  |  | 	sc := setupHTTPServerWithCfg(t, true, cfg) | 
					
						
							| 
									
										
										
										
											2022-03-17 20:07:20 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	_, err := sc.db.CreateOrgWithMember("TestOrg", testUserID) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setInitCtxSignedInOrgAdmin(sc.initCtx) | 
					
						
							|  |  |  | 	input := strings.NewReader(testPatchUserPreferencesCmd) | 
					
						
							|  |  |  | 	t.Run("Returns 200 on success", func(t *testing.T) { | 
					
						
							|  |  |  | 		response := callAPI(sc.server, http.MethodPatch, patchUserPreferencesUrl, input, t) | 
					
						
							|  |  |  | 		assert.Equal(t, http.StatusOK, response.Code) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	input = strings.NewReader(testPatchUserPreferencesCmdBad) | 
					
						
							|  |  |  | 	t.Run("Returns 400 with bad data", func(t *testing.T) { | 
					
						
							|  |  |  | 		response := callAPI(sc.server, http.MethodPut, patchUserPreferencesUrl, input, t) | 
					
						
							|  |  |  | 		assert.Equal(t, http.StatusBadRequest, response.Code) | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-04-29 20:37:33 +08:00
										 |  |  | 	input = strings.NewReader(testUpdateOrgPreferencesWithHomeDashboardUIDCmd) | 
					
						
							| 
									
										
										
										
											2022-05-23 23:14:27 +08:00
										 |  |  | 	dashSvc := dashboards.NewFakeDashboardService(t) | 
					
						
							|  |  |  | 	dashSvc.On("GetDashboard", mock.Anything, mock.AnythingOfType("*models.GetDashboardQuery")).Run(func(args mock.Arguments) { | 
					
						
							|  |  |  | 		q := args.Get(1).(*models.GetDashboardQuery) | 
					
						
							|  |  |  | 		q.Result = &models.Dashboard{Uid: "home", Id: 1} | 
					
						
							|  |  |  | 	}).Return(nil) | 
					
						
							| 
									
										
										
										
											2022-07-07 02:42:39 +08:00
										 |  |  | 	sc.hs.DashboardService = dashSvc | 
					
						
							| 
									
										
										
										
											2022-04-29 20:37:33 +08:00
										 |  |  | 	t.Run("Returns 200 on success", func(t *testing.T) { | 
					
						
							|  |  |  | 		response := callAPI(sc.server, http.MethodPatch, patchUserPreferencesUrl, input, t) | 
					
						
							|  |  |  | 		assert.Equal(t, http.StatusOK, response.Code) | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-03-17 20:07:20 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestAPIEndpoint_PatchOrgPreferences(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2022-08-11 21:37:31 +08:00
										 |  |  | 	cfg := setting.NewCfg() | 
					
						
							|  |  |  | 	cfg.RBACEnabled = false | 
					
						
							|  |  |  | 	sc := setupHTTPServerWithCfg(t, true, cfg) | 
					
						
							| 
									
										
										
										
											2022-03-17 20:07:20 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	_, err := sc.db.CreateOrgWithMember("TestOrg", testUserID) | 
					
						
							|  |  |  | 	require.NoError(t, err) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	setInitCtxSignedInOrgAdmin(sc.initCtx) | 
					
						
							|  |  |  | 	input := strings.NewReader(testPatchOrgPreferencesCmd) | 
					
						
							|  |  |  | 	t.Run("Returns 200 on success", func(t *testing.T) { | 
					
						
							|  |  |  | 		response := callAPI(sc.server, http.MethodPatch, patchOrgPreferencesUrl, input, t) | 
					
						
							|  |  |  | 		assert.Equal(t, http.StatusOK, response.Code) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	input = strings.NewReader(testPatchOrgPreferencesCmdBad) | 
					
						
							|  |  |  | 	t.Run("Returns 400 with bad data", func(t *testing.T) { | 
					
						
							|  |  |  | 		response := callAPI(sc.server, http.MethodPut, patchOrgPreferencesUrl, input, t) | 
					
						
							|  |  |  | 		assert.Equal(t, http.StatusBadRequest, response.Code) | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } |