| 
									
										
										
										
											2021-08-04 20:44:37 +08:00
										 |  |  | package api | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2021-11-03 18:31:56 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2023-01-19 20:49:57 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2021-08-04 20:44:37 +08:00
										 |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-19 20:49:57 +08:00
										 |  |  | 	"github.com/stretchr/testify/assert" | 
					
						
							|  |  |  | 	"github.com/stretchr/testify/require" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-04 20:44:37 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/accesscontrol" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/provisioning" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/setting" | 
					
						
							| 
									
										
										
										
											2023-01-19 20:49:57 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/web/webtest" | 
					
						
							| 
									
										
										
										
											2021-08-04 20:44:37 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestAPI_AdminProvisioningReload_AccessControl(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2023-01-19 20:49:57 +08:00
										 |  |  | 	type testCase struct { | 
					
						
							|  |  |  | 		desc         string | 
					
						
							|  |  |  | 		url          string | 
					
						
							|  |  |  | 		expectedBody string | 
					
						
							|  |  |  | 		expectedCode int | 
					
						
							|  |  |  | 		permissions  []accesscontrol.Permission | 
					
						
							|  |  |  | 		checkCall    func(mock provisioning.ProvisioningServiceMock) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	tests := []testCase{ | 
					
						
							| 
									
										
										
										
											2021-08-04 20:44:37 +08:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			desc:         "should work for dashboards with specific scope", | 
					
						
							|  |  |  | 			expectedCode: http.StatusOK, | 
					
						
							|  |  |  | 			expectedBody: `{"message":"Dashboards config reloaded"}`, | 
					
						
							| 
									
										
										
										
											2022-06-14 16:17:48 +08:00
										 |  |  | 			permissions: []accesscontrol.Permission{ | 
					
						
							| 
									
										
										
										
											2021-08-04 20:44:37 +08:00
										 |  |  | 				{ | 
					
						
							|  |  |  | 					Action: ActionProvisioningReload, | 
					
						
							|  |  |  | 					Scope:  ScopeProvisionersDashboards, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			url: "/api/admin/provisioning/dashboards/reload", | 
					
						
							|  |  |  | 			checkCall: func(mock provisioning.ProvisioningServiceMock) { | 
					
						
							|  |  |  | 				assert.Len(t, mock.Calls.ProvisionDashboards, 1) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			desc:         "should work for dashboards with broader scope", | 
					
						
							|  |  |  | 			expectedCode: http.StatusOK, | 
					
						
							|  |  |  | 			expectedBody: `{"message":"Dashboards config reloaded"}`, | 
					
						
							| 
									
										
										
										
											2022-06-14 16:17:48 +08:00
										 |  |  | 			permissions: []accesscontrol.Permission{ | 
					
						
							| 
									
										
										
										
											2021-08-04 20:44:37 +08:00
										 |  |  | 				{ | 
					
						
							|  |  |  | 					Action: ActionProvisioningReload, | 
					
						
							|  |  |  | 					Scope:  ScopeProvisionersAll, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			url: "/api/admin/provisioning/dashboards/reload", | 
					
						
							|  |  |  | 			checkCall: func(mock provisioning.ProvisioningServiceMock) { | 
					
						
							|  |  |  | 				assert.Len(t, mock.Calls.ProvisionDashboards, 1) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			desc:         "should fail for dashboard with wrong scope", | 
					
						
							|  |  |  | 			expectedCode: http.StatusForbidden, | 
					
						
							| 
									
										
										
										
											2022-06-14 16:17:48 +08:00
										 |  |  | 			permissions: []accesscontrol.Permission{ | 
					
						
							| 
									
										
										
										
											2021-08-04 20:44:37 +08:00
										 |  |  | 				{ | 
					
						
							|  |  |  | 					Action: ActionProvisioningReload, | 
					
						
							|  |  |  | 					Scope:  "services:noservice", | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2023-01-19 20:49:57 +08:00
										 |  |  | 			url: "/api/admin/provisioning/dashboards/reload", | 
					
						
							| 
									
										
										
										
											2021-08-04 20:44:37 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			desc:         "should fail for dashboard with no permission", | 
					
						
							|  |  |  | 			expectedCode: http.StatusForbidden, | 
					
						
							|  |  |  | 			url:          "/api/admin/provisioning/dashboards/reload", | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			desc:         "should work for datasources with specific scope", | 
					
						
							|  |  |  | 			expectedCode: http.StatusOK, | 
					
						
							|  |  |  | 			expectedBody: `{"message":"Datasources config reloaded"}`, | 
					
						
							| 
									
										
										
										
											2022-06-14 16:17:48 +08:00
										 |  |  | 			permissions: []accesscontrol.Permission{ | 
					
						
							| 
									
										
										
										
											2021-08-04 20:44:37 +08:00
										 |  |  | 				{ | 
					
						
							|  |  |  | 					Action: ActionProvisioningReload, | 
					
						
							|  |  |  | 					Scope:  ScopeProvisionersDatasources, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			url: "/api/admin/provisioning/datasources/reload", | 
					
						
							|  |  |  | 			checkCall: func(mock provisioning.ProvisioningServiceMock) { | 
					
						
							|  |  |  | 				assert.Len(t, mock.Calls.ProvisionDatasources, 1) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			desc:         "should fail for datasources with no permission", | 
					
						
							|  |  |  | 			expectedCode: http.StatusForbidden, | 
					
						
							|  |  |  | 			url:          "/api/admin/provisioning/datasources/reload", | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			desc:         "should work for plugins with specific scope", | 
					
						
							|  |  |  | 			expectedCode: http.StatusOK, | 
					
						
							|  |  |  | 			expectedBody: `{"message":"Plugins config reloaded"}`, | 
					
						
							| 
									
										
										
										
											2022-06-14 16:17:48 +08:00
										 |  |  | 			permissions: []accesscontrol.Permission{ | 
					
						
							| 
									
										
										
										
											2021-08-04 20:44:37 +08:00
										 |  |  | 				{ | 
					
						
							|  |  |  | 					Action: ActionProvisioningReload, | 
					
						
							|  |  |  | 					Scope:  ScopeProvisionersPlugins, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			url: "/api/admin/provisioning/plugins/reload", | 
					
						
							|  |  |  | 			checkCall: func(mock provisioning.ProvisioningServiceMock) { | 
					
						
							|  |  |  | 				assert.Len(t, mock.Calls.ProvisionPlugins, 1) | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			desc:         "should fail for plugins with no permission", | 
					
						
							|  |  |  | 			expectedCode: http.StatusForbidden, | 
					
						
							|  |  |  | 			url:          "/api/admin/provisioning/plugins/reload", | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2022-07-15 05:53:13 +08:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			desc:         "should fail for alerting with no permission", | 
					
						
							|  |  |  | 			expectedCode: http.StatusForbidden, | 
					
						
							|  |  |  | 			url:          "/api/admin/provisioning/alerting/reload", | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			desc:         "should work for alert rules with specific scope", | 
					
						
							|  |  |  | 			expectedCode: http.StatusOK, | 
					
						
							|  |  |  | 			expectedBody: `{"message":"Alerting config reloaded"}`, | 
					
						
							|  |  |  | 			permissions: []accesscontrol.Permission{ | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					Action: ActionProvisioningReload, | 
					
						
							|  |  |  | 					Scope:  ScopeProvisionersAlertRules, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			url: "/api/admin/provisioning/alerting/reload", | 
					
						
							|  |  |  | 			checkCall: func(mock provisioning.ProvisioningServiceMock) { | 
					
						
							| 
									
										
										
										
											2022-08-02 00:17:42 +08:00
										 |  |  | 				assert.Len(t, mock.Calls.ProvisionAlerting, 1) | 
					
						
							| 
									
										
										
										
											2022-07-15 05:53:13 +08:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			desc:         "should fail for alerting with no permission", | 
					
						
							|  |  |  | 			expectedCode: http.StatusForbidden, | 
					
						
							|  |  |  | 			url:          "/api/admin/provisioning/alerting/reload", | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2021-08-04 20:44:37 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-19 20:49:57 +08:00
										 |  |  | 	for _, tt := range tests { | 
					
						
							|  |  |  | 		t.Run(tt.desc, func(t *testing.T) { | 
					
						
							|  |  |  | 			pService := provisioning.NewProvisioningServiceMock(context.Background()) | 
					
						
							|  |  |  | 			server := SetupAPITestServer(t, func(hs *HTTPServer) { | 
					
						
							|  |  |  | 				hs.Cfg = setting.NewCfg() | 
					
						
							|  |  |  | 				hs.ProvisioningService = pService | 
					
						
							|  |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2021-08-04 20:44:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-19 20:49:57 +08:00
										 |  |  | 			res, err := server.Send(webtest.RequestWithSignedInUser(server.NewPostRequest(tt.url, nil), userWithPermissions(1, tt.permissions))) | 
					
						
							|  |  |  | 			require.NoError(t, err) | 
					
						
							|  |  |  | 			assert.Equal(t, tt.expectedCode, res.StatusCode) | 
					
						
							| 
									
										
										
										
											2021-08-04 20:44:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-19 20:49:57 +08:00
										 |  |  | 			if tt.expectedCode == http.StatusOK { | 
					
						
							|  |  |  | 				body, err := io.ReadAll(res.Body) | 
					
						
							|  |  |  | 				require.NoError(t, err) | 
					
						
							|  |  |  | 				assert.Equal(t, tt.expectedBody, string(body)) | 
					
						
							| 
									
										
										
										
											2021-08-04 20:44:37 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-19 20:49:57 +08:00
										 |  |  | 			require.NoError(t, res.Body.Close()) | 
					
						
							| 
									
										
										
										
											2021-08-04 20:44:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-19 20:49:57 +08:00
										 |  |  | 			if tt.checkCall != nil { | 
					
						
							|  |  |  | 				// Check we actually called the provisioning service
 | 
					
						
							|  |  |  | 				tt.checkCall(*pService) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-08-04 20:44:37 +08:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |