diff --git a/pkg/api/api.go b/pkg/api/api.go index c0bf807941d..78a8c08e283 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -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)) diff --git a/pkg/api/datasources.go b/pkg/api/datasources.go index aeaf51c8c9a..28989cb174e 100644 --- a/pkg/api/datasources.go +++ b/pkg/api/datasources.go @@ -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 +} diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index 49932626ddb..5919ae77a5a 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -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) diff --git a/pkg/api/roles.go b/pkg/api/roles.go index 16f0d7c6cd6..df14fae699c 100644 --- a/pkg/api/roles.go +++ b/pkg/api/roles.go @@ -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)},