mirror of
https://github.com/grafana/grafana.git
synced 2025-07-30 03:12:59 +08:00
Access Control: Allow signed in users access to GET data sources endpoints (#43338)
* remove scopes from endpoints and add datasources:read without scope to the compatibility role
This commit is contained in:
@ -266,15 +266,15 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
|
||||
// Data sources
|
||||
apiRoute.Group("/datasources", func(datasourceRoute routing.RouteRegister) {
|
||||
datasourceRoute.Get("/", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesRead, ScopeDatasourcesAll)), routing.Wrap(hs.GetDataSources))
|
||||
datasourceRoute.Get("/", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesRead)), routing.Wrap(hs.GetDataSources))
|
||||
datasourceRoute.Post("/", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesCreate)), quota("data_source"), routing.Wrap(AddDataSource))
|
||||
datasourceRoute.Put("/:id", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesWrite, ScopeDatasourceID)), routing.Wrap(hs.UpdateDataSource))
|
||||
datasourceRoute.Delete("/:id", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesDelete, ScopeDatasourceID)), routing.Wrap(hs.DeleteDataSourceById))
|
||||
datasourceRoute.Delete("/uid/:uid", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesDelete, ScopeDatasourceUID)), routing.Wrap(hs.DeleteDataSourceByUID))
|
||||
datasourceRoute.Delete("/name/:name", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesDelete, ScopeDatasourceName)), routing.Wrap(hs.DeleteDataSourceByName))
|
||||
datasourceRoute.Get("/:id", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesRead, ScopeDatasourceID)), routing.Wrap(hs.GetDataSourceById))
|
||||
datasourceRoute.Get("/uid/:uid", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesRead, ScopeDatasourceUID)), routing.Wrap(hs.GetDataSourceByUID))
|
||||
datasourceRoute.Get("/name/:name", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesRead, ScopeDatasourceName)), routing.Wrap(GetDataSourceByName))
|
||||
datasourceRoute.Get("/:id", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesRead)), routing.Wrap(hs.GetDataSourceById))
|
||||
datasourceRoute.Get("/uid/:uid", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesRead)), routing.Wrap(hs.GetDataSourceByUID))
|
||||
datasourceRoute.Get("/name/:name", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesRead)), routing.Wrap(GetDataSourceByName))
|
||||
})
|
||||
|
||||
apiRoute.Get("/datasources/id/:name", authorize(reqSignedIn, ac.EvalPermission(ActionDatasourcesIDRead, ScopeDatasourceName)), routing.Wrap(GetDataSourceIdByName))
|
||||
|
@ -31,8 +31,13 @@ func (hs *HTTPServer) GetDataSources(c *models.ReqContext) response.Response {
|
||||
return response.Error(500, "Failed to query datasources", err)
|
||||
}
|
||||
|
||||
filtered, err := filterDatasourcesByQueryPermission(c.Req.Context(), c.SignedInUser, query.Result)
|
||||
if err != nil {
|
||||
return response.Error(500, "Failed to query datasources", err)
|
||||
}
|
||||
|
||||
result := make(dtos.DataSourceList, 0)
|
||||
for _, ds := range query.Result {
|
||||
for _, ds := range filtered {
|
||||
dsItem := dtos.DataSourceListItemDTO{
|
||||
OrgId: ds.OrgId,
|
||||
Id: ds.Id,
|
||||
@ -102,17 +107,20 @@ func (hs *HTTPServer) GetDataSourceById(c *models.ReqContext) response.Response
|
||||
return response.Error(500, "Failed to query datasources", err)
|
||||
}
|
||||
|
||||
ds := query.Result
|
||||
dtos := convertModelToDtos(ds)
|
||||
filtered, err := filterDatasourcesByQueryPermission(c.Req.Context(), c.SignedInUser, []*models.DataSource{query.Result})
|
||||
if err != nil || len(filtered) != 1 {
|
||||
return response.Error(404, "Data source not found", err)
|
||||
}
|
||||
|
||||
dto := convertModelToDtos(filtered[0])
|
||||
// Add accesscontrol metadata
|
||||
metadata, err := hs.getDataSourceAccessControlMetadata(c, ds.Id)
|
||||
metadata, err := hs.getDataSourceAccessControlMetadata(c, dto.Id)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to query metadata", err)
|
||||
}
|
||||
dtos.AccessControl = metadata
|
||||
dto.AccessControl = metadata
|
||||
|
||||
return response.JSON(200, &dtos)
|
||||
return response.JSON(200, &dto)
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) DeleteDataSourceById(c *models.ReqContext) response.Response {
|
||||
@ -160,16 +168,21 @@ func (hs *HTTPServer) GetDataSourceByUID(c *models.ReqContext) response.Response
|
||||
return response.Error(http.StatusInternalServerError, "Failed to query datasource", err)
|
||||
}
|
||||
|
||||
dtos := convertModelToDtos(ds)
|
||||
filtered, err := filterDatasourcesByQueryPermission(c.Req.Context(), c.SignedInUser, []*models.DataSource{ds})
|
||||
if err != nil || len(filtered) != 1 {
|
||||
return response.Error(404, "Data source not found", err)
|
||||
}
|
||||
|
||||
dto := convertModelToDtos(filtered[0])
|
||||
|
||||
// Add accesscontrol metadata
|
||||
metadata, err := hs.getDataSourceAccessControlMetadata(c, ds.Id)
|
||||
metadata, err := hs.getDataSourceAccessControlMetadata(c, dto.Id)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to query metadata", err)
|
||||
}
|
||||
dtos.AccessControl = metadata
|
||||
dto.AccessControl = metadata
|
||||
|
||||
return response.JSON(200, &dtos)
|
||||
return response.JSON(200, &dto)
|
||||
}
|
||||
|
||||
// DELETE /api/datasources/uid/:uid
|
||||
@ -397,8 +410,13 @@ func GetDataSourceByName(c *models.ReqContext) response.Response {
|
||||
return response.Error(500, "Failed to query datasources", err)
|
||||
}
|
||||
|
||||
dtos := convertModelToDtos(query.Result)
|
||||
return response.JSON(200, &dtos)
|
||||
filtered, err := filterDatasourcesByQueryPermission(c.Req.Context(), c.SignedInUser, []*models.DataSource{query.Result})
|
||||
if err != nil || len(filtered) != 1 {
|
||||
return response.Error(404, "Data source not found", err)
|
||||
}
|
||||
|
||||
dto := convertModelToDtos(filtered[0])
|
||||
return response.JSON(200, &dto)
|
||||
}
|
||||
|
||||
// Get /api/datasources/id/:name
|
||||
@ -549,3 +567,19 @@ func (hs *HTTPServer) decryptSecureJsonDataFn() func(map[string][]byte) map[stri
|
||||
return decryptedJsonData
|
||||
}
|
||||
}
|
||||
|
||||
func filterDatasourcesByQueryPermission(ctx context.Context, user *models.SignedInUser, datasources []*models.DataSource) ([]*models.DataSource, error) {
|
||||
query := models.DatasourcesPermissionFilterQuery{
|
||||
User: user,
|
||||
Datasources: datasources,
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(ctx, &query); err != nil {
|
||||
if !errors.Is(err, bus.ErrHandlerNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
return datasources, nil
|
||||
}
|
||||
|
||||
return query.Datasources, nil
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
@ -25,20 +24,12 @@ func (hs *HTTPServer) getFSDataSources(c *models.ReqContext, enabledPlugins Enab
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dsFilterQuery := models.DatasourcesPermissionFilterQuery{
|
||||
User: c.SignedInUser,
|
||||
Datasources: query.Result,
|
||||
filtered, err := filterDatasourcesByQueryPermission(c.Req.Context(), c.SignedInUser, query.Result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := bus.Dispatch(c.Req.Context(), &dsFilterQuery); err != nil {
|
||||
if !errors.Is(err, bus.ErrHandlerNotFound) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
orgDataSources = query.Result
|
||||
} else {
|
||||
orgDataSources = dsFilterQuery.Result
|
||||
}
|
||||
orgDataSources = filtered
|
||||
}
|
||||
|
||||
dataSources := make(map[string]plugins.DataSourceDTO)
|
||||
|
@ -134,6 +134,7 @@ func (hs *HTTPServer) declareFixedRoles() error {
|
||||
Group: "Infrequently used",
|
||||
Permissions: []accesscontrol.Permission{
|
||||
{Action: ActionDatasourcesQuery},
|
||||
{Action: ActionDatasourcesRead},
|
||||
},
|
||||
},
|
||||
Grants: []string{string(models.ROLE_VIEWER)},
|
||||
|
Reference in New Issue
Block a user