mirror of https://github.com/grafana/grafana.git
				
				
				
			Team LBAC: Add permission check for Update datasource (#77709)
* add permission check for updating the LBAC Rules * permission scoped for id in the updating datasource * fixed test to cover for permissions * fix proper check for permissions and empty teamHTTPHeader requests * check for jsondata * check nil for jsondata inside the getEncodedString
This commit is contained in:
		
							parent
							
								
									a39242890e
								
							
						
					
					
						commit
						c13fd62b16
					
				|  | @ -20,6 +20,7 @@ import ( | ||||||
| 	"github.com/grafana/grafana/pkg/api/response" | 	"github.com/grafana/grafana/pkg/api/response" | ||||||
| 	"github.com/grafana/grafana/pkg/components/simplejson" | 	"github.com/grafana/grafana/pkg/components/simplejson" | ||||||
| 	"github.com/grafana/grafana/pkg/infra/log" | 	"github.com/grafana/grafana/pkg/infra/log" | ||||||
|  | 	ac "github.com/grafana/grafana/pkg/services/accesscontrol" | ||||||
| 	"github.com/grafana/grafana/pkg/services/auth/identity" | 	"github.com/grafana/grafana/pkg/services/auth/identity" | ||||||
| 	contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" | 	contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model" | ||||||
| 	"github.com/grafana/grafana/pkg/services/datasources" | 	"github.com/grafana/grafana/pkg/services/datasources" | ||||||
|  | @ -409,6 +410,11 @@ func validateLBACHeader(headervalue string) bool { | ||||||
| 	return err == nil | 	return err == nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func evaluateTeamHTTPHeaderPermissions(hs *HTTPServer, c *contextmodel.ReqContext, scope string) (bool, error) { | ||||||
|  | 	ev := ac.EvalPermission(datasources.ActionPermissionsWrite, ac.Scope(scope)) | ||||||
|  | 	return hs.AccessControl.Evaluate(c.Req.Context(), c.SignedInUser, ev) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // swagger:route POST /datasources datasources addDataSource
 | // swagger:route POST /datasources datasources addDataSource
 | ||||||
| //
 | //
 | ||||||
| // Create a data source.
 | // Create a data source.
 | ||||||
|  | @ -446,6 +452,7 @@ func (hs *HTTPServer) AddDataSource(c *contextmodel.ReqContext) response.Respons | ||||||
| 			return resp | 			return resp | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
| 	if err := validateJSONData(c.Req.Context(), cmd.JsonData, hs.Cfg, hs.Features); err != nil { | 	if err := validateJSONData(c.Req.Context(), cmd.JsonData, hs.Cfg, hs.Features); err != nil { | ||||||
| 		return response.Error(http.StatusBadRequest, "Failed to add datasource", err) | 		return response.Error(http.StatusBadRequest, "Failed to add datasource", err) | ||||||
| 	} | 	} | ||||||
|  | @ -514,7 +521,6 @@ func (hs *HTTPServer) UpdateDataSourceByID(c *contextmodel.ReqContext) response. | ||||||
| 	if err := validateJSONData(c.Req.Context(), cmd.JsonData, hs.Cfg, hs.Features); err != nil { | 	if err := validateJSONData(c.Req.Context(), cmd.JsonData, hs.Cfg, hs.Features); err != nil { | ||||||
| 		return response.Error(http.StatusBadRequest, "Failed to update datasource", err) | 		return response.Error(http.StatusBadRequest, "Failed to update datasource", err) | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	ds, err := hs.getRawDataSourceById(c.Req.Context(), cmd.ID, cmd.OrgID) | 	ds, err := hs.getRawDataSourceById(c.Req.Context(), cmd.ID, cmd.OrgID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if errors.Is(err, datasources.ErrDataSourceNotFound) { | 		if errors.Is(err, datasources.ErrDataSourceNotFound) { | ||||||
|  | @ -522,6 +528,13 @@ func (hs *HTTPServer) UpdateDataSourceByID(c *contextmodel.ReqContext) response. | ||||||
| 		} | 		} | ||||||
| 		return response.Error(500, "Failed to update datasource", err) | 		return response.Error(500, "Failed to update datasource", err) | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	// check if LBAC rules have been modified
 | ||||||
|  | 	hasAccess, errAccess := checkTeamHTTPHeaderPermissions(hs, c, ds, cmd) | ||||||
|  | 	if !hasAccess { | ||||||
|  | 		return response.Error(http.StatusForbidden, fmt.Sprintf("You'll need additional permissions to perform this action. Permissions needed: %s", datasources.ActionPermissionsWrite), errAccess) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return hs.updateDataSourceByID(c, ds, cmd) | 	return hs.updateDataSourceByID(c, ds, cmd) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -563,9 +576,37 @@ func (hs *HTTPServer) UpdateDataSourceByUID(c *contextmodel.ReqContext) response | ||||||
| 		return response.Error(http.StatusInternalServerError, "Failed to update datasource", err) | 		return response.Error(http.StatusInternalServerError, "Failed to update datasource", err) | ||||||
| 	} | 	} | ||||||
| 	cmd.ID = ds.ID | 	cmd.ID = ds.ID | ||||||
|  | 
 | ||||||
|  | 	// check if LBAC rules have been modified
 | ||||||
|  | 	hasAccess, errAccess := checkTeamHTTPHeaderPermissions(hs, c, ds, cmd) | ||||||
|  | 	if !hasAccess { | ||||||
|  | 		return response.Error(http.StatusForbidden, fmt.Sprintf("You'll need additional permissions to perform this action. Permissions needed: %s", datasources.ActionPermissionsWrite), errAccess) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return hs.updateDataSourceByID(c, ds, cmd) | 	return hs.updateDataSourceByID(c, ds, cmd) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func getEncodedString(jsonData *simplejson.Json, key string) string { | ||||||
|  | 	if jsonData == nil { | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  | 	jsonValues, exists := jsonData.CheckGet(key) | ||||||
|  | 	if !exists { | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  | 	val, _ := jsonValues.Encode() | ||||||
|  | 	return string(val) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func checkTeamHTTPHeaderPermissions(hs *HTTPServer, c *contextmodel.ReqContext, ds *datasources.DataSource, cmd datasources.UpdateDataSourceCommand) (bool, error) { | ||||||
|  | 	currentTeamHTTPHeaders := getEncodedString(ds.JsonData, "teamHttpHeaders") | ||||||
|  | 	newTeamHTTPHeaders := getEncodedString(cmd.JsonData, "teamHttpHeaders") | ||||||
|  | 	if (currentTeamHTTPHeaders != "" || newTeamHTTPHeaders != "") && currentTeamHTTPHeaders != newTeamHTTPHeaders { | ||||||
|  | 		return evaluateTeamHTTPHeaderPermissions(hs, c, datasources.ScopePrefix+ds.UID) | ||||||
|  | 	} | ||||||
|  | 	return true, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (hs *HTTPServer) updateDataSourceByID(c *contextmodel.ReqContext, ds *datasources.DataSource, cmd datasources.UpdateDataSourceCommand) response.Response { | func (hs *HTTPServer) updateDataSourceByID(c *contextmodel.ReqContext, ds *datasources.DataSource, cmd datasources.UpdateDataSourceCommand) response.Response { | ||||||
| 	if ds.ReadOnly { | 	if ds.ReadOnly { | ||||||
| 		return response.Error(403, "Cannot update read-only data source", nil) | 		return response.Error(403, "Cannot update read-only data source", nil) | ||||||
|  |  | ||||||
|  | @ -286,6 +286,10 @@ func TestUpdateDataSourceTeamHTTPHeaders_InvalidJSONData(t *testing.T) { | ||||||
| 				Cfg:                  setting.NewCfg(), | 				Cfg:                  setting.NewCfg(), | ||||||
| 				Features:             featuremgmt.WithFeatures(featuremgmt.FlagTeamHttpHeaders), | 				Features:             featuremgmt.WithFeatures(featuremgmt.FlagTeamHttpHeaders), | ||||||
| 				accesscontrolService: actest.FakeService{}, | 				accesscontrolService: actest.FakeService{}, | ||||||
|  | 				AccessControl: actest.FakeAccessControl{ | ||||||
|  | 					ExpectedEvaluate: true, | ||||||
|  | 					ExpectedErr:      nil, | ||||||
|  | 				}, | ||||||
| 			} | 			} | ||||||
| 			sc := setupScenarioContext(t, fmt.Sprintf("/api/datasources/%s", tenantID)) | 			sc := setupScenarioContext(t, fmt.Sprintf("/api/datasources/%s", tenantID)) | ||||||
| 			hs.Cfg.AuthProxyEnabled = true | 			hs.Cfg.AuthProxyEnabled = true | ||||||
|  | @ -300,7 +304,9 @@ func TestUpdateDataSourceTeamHTTPHeaders_InvalidJSONData(t *testing.T) { | ||||||
| 					Type:     "test", | 					Type:     "test", | ||||||
| 					JsonData: jsonData, | 					JsonData: jsonData, | ||||||
| 				}) | 				}) | ||||||
| 				c.SignedInUser = authedUserWithPermissions(1, 1, []ac.Permission{}) | 				c.SignedInUser = authedUserWithPermissions(1, 1, []ac.Permission{ | ||||||
|  | 					{Action: datasources.ActionPermissionsWrite, Scope: datasources.ScopeAll}, | ||||||
|  | 				}) | ||||||
| 				return hs.AddDataSource(c) | 				return hs.AddDataSource(c) | ||||||
| 			})) | 			})) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue