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:
Eric Leijonmarck 2023-11-08 14:37:32 +00:00 committed by GitHub
parent a39242890e
commit c13fd62b16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 49 additions and 2 deletions

View File

@ -20,6 +20,7 @@ import (
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/auth/identity"
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
"github.com/grafana/grafana/pkg/services/datasources"
@ -409,6 +410,11 @@ func validateLBACHeader(headervalue string) bool {
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
//
// Create a data source.
@ -446,6 +452,7 @@ func (hs *HTTPServer) AddDataSource(c *contextmodel.ReqContext) response.Respons
return resp
}
}
if err := validateJSONData(c.Req.Context(), cmd.JsonData, hs.Cfg, hs.Features); err != nil {
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 {
return response.Error(http.StatusBadRequest, "Failed to update datasource", err)
}
ds, err := hs.getRawDataSourceById(c.Req.Context(), cmd.ID, cmd.OrgID)
if err != nil {
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)
}
// 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)
}
@ -563,9 +576,37 @@ func (hs *HTTPServer) UpdateDataSourceByUID(c *contextmodel.ReqContext) response
return response.Error(http.StatusInternalServerError, "Failed to update datasource", err)
}
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)
}
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 {
if ds.ReadOnly {
return response.Error(403, "Cannot update read-only data source", nil)

View File

@ -286,6 +286,10 @@ func TestUpdateDataSourceTeamHTTPHeaders_InvalidJSONData(t *testing.T) {
Cfg: setting.NewCfg(),
Features: featuremgmt.WithFeatures(featuremgmt.FlagTeamHttpHeaders),
accesscontrolService: actest.FakeService{},
AccessControl: actest.FakeAccessControl{
ExpectedEvaluate: true,
ExpectedErr: nil,
},
}
sc := setupScenarioContext(t, fmt.Sprintf("/api/datasources/%s", tenantID))
hs.Cfg.AuthProxyEnabled = true
@ -300,7 +304,9 @@ func TestUpdateDataSourceTeamHTTPHeaders_InvalidJSONData(t *testing.T) {
Type: "test",
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)
}))