Move datasource scopes and actions to access control package (#46334)
* create scope provider * move datasource actions and scopes to datasource package + add provider * change usages to use datasource scopes and update data source name resolver to use provider * move folder permissions to dashboard package and update usages
This commit is contained in:
+38
-48
@@ -5,6 +5,8 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
@@ -12,13 +14,6 @@ import (
|
||||
const (
|
||||
ActionProvisioningReload = "provisioning:reload"
|
||||
|
||||
ActionDatasourcesRead = "datasources:read"
|
||||
ActionDatasourcesQuery = "datasources:query"
|
||||
ActionDatasourcesCreate = "datasources:create"
|
||||
ActionDatasourcesWrite = "datasources:write"
|
||||
ActionDatasourcesDelete = "datasources:delete"
|
||||
ActionDatasourcesIDRead = "datasources.id:read"
|
||||
|
||||
ActionOrgsRead = "orgs:read"
|
||||
ActionOrgsPreferencesRead = "orgs.preferences:read"
|
||||
ActionOrgsQuotasRead = "orgs.quotas:read"
|
||||
@@ -36,11 +31,6 @@ var (
|
||||
ScopeProvisionersPlugins = ac.Scope("provisioners", "plugins")
|
||||
ScopeProvisionersDatasources = ac.Scope("provisioners", "datasources")
|
||||
ScopeProvisionersNotifications = ac.Scope("provisioners", "notifications")
|
||||
|
||||
ScopeDatasourcesAll = ac.Scope("datasources", "*")
|
||||
ScopeDatasourceID = ac.Scope("datasources", "id", ac.Parameter(":id"))
|
||||
ScopeDatasourceUID = ac.Scope("datasources", "uid", ac.Parameter(":uid"))
|
||||
ScopeDatasourceName = ac.Scope("datasources", "name", ac.Parameter(":name"))
|
||||
)
|
||||
|
||||
// declareFixedRoles declares to the AccessControl service fixed roles and their
|
||||
@@ -93,12 +83,12 @@ func (hs *HTTPServer) declareFixedRoles() error {
|
||||
Group: "Data sources",
|
||||
Permissions: []ac.Permission{
|
||||
{
|
||||
Action: ActionDatasourcesRead,
|
||||
Scope: ScopeDatasourcesAll,
|
||||
Action: datasources.ActionDatasourcesRead,
|
||||
Scope: datasources.ScopeDatasourcesProvider.GetResourceAllScope(),
|
||||
},
|
||||
{
|
||||
Action: ActionDatasourcesQuery,
|
||||
Scope: ScopeDatasourcesAll,
|
||||
Action: datasources.ActionDatasourcesQuery,
|
||||
Scope: datasources.ScopeDatasourcesAll,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -114,15 +104,15 @@ func (hs *HTTPServer) declareFixedRoles() error {
|
||||
Group: "Data sources",
|
||||
Permissions: ac.ConcatPermissions(datasourcesReaderRole.Role.Permissions, []ac.Permission{
|
||||
{
|
||||
Action: ActionDatasourcesWrite,
|
||||
Scope: ScopeDatasourcesAll,
|
||||
Action: datasources.ActionDatasourcesWrite,
|
||||
Scope: datasources.ScopeDatasourcesAll,
|
||||
},
|
||||
{
|
||||
Action: ActionDatasourcesCreate,
|
||||
Action: datasources.ActionDatasourcesCreate,
|
||||
},
|
||||
{
|
||||
Action: ActionDatasourcesDelete,
|
||||
Scope: ScopeDatasourcesAll,
|
||||
Action: datasources.ActionDatasourcesDelete,
|
||||
Scope: datasources.ScopeDatasourcesAll,
|
||||
},
|
||||
}),
|
||||
},
|
||||
@@ -138,8 +128,8 @@ func (hs *HTTPServer) declareFixedRoles() error {
|
||||
Group: "Infrequently used",
|
||||
Permissions: []ac.Permission{
|
||||
{
|
||||
Action: ActionDatasourcesIDRead,
|
||||
Scope: ScopeDatasourcesAll,
|
||||
Action: datasources.ActionDatasourcesIDRead,
|
||||
Scope: datasources.ScopeDatasourcesAll,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -154,8 +144,8 @@ func (hs *HTTPServer) declareFixedRoles() error {
|
||||
Description: "Only used for open source compatibility. Query data sources.",
|
||||
Group: "Infrequently used",
|
||||
Permissions: []ac.Permission{
|
||||
{Action: ActionDatasourcesQuery},
|
||||
{Action: ActionDatasourcesRead},
|
||||
{Action: datasources.ActionDatasourcesQuery},
|
||||
{Action: datasources.ActionDatasourcesRead},
|
||||
},
|
||||
},
|
||||
Grants: []string{string(models.ROLE_VIEWER)},
|
||||
@@ -294,8 +284,8 @@ func (hs *HTTPServer) declareFixedRoles() error {
|
||||
Description: "Create dashboard in general folder.",
|
||||
Group: "Dashboards",
|
||||
Permissions: []ac.Permission{
|
||||
{Action: ac.ActionFoldersRead, Scope: ac.Scope("folders", "id", "0")},
|
||||
{Action: ac.ActionDashboardsCreate, Scope: ac.Scope("folders", "id", "0")},
|
||||
{Action: dashboards.ActionFoldersRead, Scope: dashboards.ScopeFoldersProvider.GetResourceScope("0")},
|
||||
{Action: ac.ActionDashboardsCreate, Scope: dashboards.ScopeFoldersProvider.GetResourceScope("0")},
|
||||
},
|
||||
},
|
||||
Grants: []string{"Editor"},
|
||||
@@ -325,7 +315,7 @@ func (hs *HTTPServer) declareFixedRoles() error {
|
||||
Permissions: ac.ConcatPermissions(dashboardsReaderRole.Role.Permissions, []ac.Permission{
|
||||
{Action: ac.ActionDashboardsWrite, Scope: ac.ScopeDashboardsAll},
|
||||
{Action: ac.ActionDashboardsDelete, Scope: ac.ScopeDashboardsAll},
|
||||
{Action: ac.ActionDashboardsCreate, Scope: ac.ScopeFoldersAll},
|
||||
{Action: ac.ActionDashboardsCreate, Scope: dashboards.ScopeFoldersAll},
|
||||
{Action: ac.ActionDashboardsPermissionsRead, Scope: ac.ScopeDashboardsAll},
|
||||
{Action: ac.ActionDashboardsPermissionsWrite, Scope: ac.ScopeDashboardsAll},
|
||||
}),
|
||||
@@ -341,7 +331,7 @@ func (hs *HTTPServer) declareFixedRoles() error {
|
||||
Description: "Create folders.",
|
||||
Group: "Folders",
|
||||
Permissions: []ac.Permission{
|
||||
{Action: ac.ActionFoldersCreate},
|
||||
{Action: dashboards.ActionFoldersCreate},
|
||||
},
|
||||
},
|
||||
Grants: []string{"Editor"},
|
||||
@@ -355,8 +345,8 @@ func (hs *HTTPServer) declareFixedRoles() error {
|
||||
Description: "Read all folders and dashboards.",
|
||||
Group: "Folders",
|
||||
Permissions: []ac.Permission{
|
||||
{Action: ac.ActionFoldersRead, Scope: ac.ScopeFoldersAll},
|
||||
{Action: ac.ActionDashboardsRead, Scope: ac.ScopeFoldersAll},
|
||||
{Action: dashboards.ActionFoldersRead, Scope: dashboards.ScopeFoldersAll},
|
||||
{Action: ac.ActionDashboardsRead, Scope: dashboards.ScopeFoldersAll},
|
||||
},
|
||||
},
|
||||
Grants: []string{"Admin"},
|
||||
@@ -372,14 +362,14 @@ func (hs *HTTPServer) declareFixedRoles() error {
|
||||
Permissions: ac.ConcatPermissions(
|
||||
foldersReaderRole.Role.Permissions,
|
||||
[]ac.Permission{
|
||||
{Action: ac.ActionFoldersCreate},
|
||||
{Action: ac.ActionFoldersWrite, Scope: ac.ScopeFoldersAll},
|
||||
{Action: ac.ActionFoldersDelete, Scope: ac.ScopeFoldersAll},
|
||||
{Action: ac.ActionDashboardsWrite, Scope: ac.ScopeFoldersAll},
|
||||
{Action: ac.ActionDashboardsDelete, Scope: ac.ScopeFoldersAll},
|
||||
{Action: ac.ActionDashboardsCreate, Scope: ac.ScopeFoldersAll},
|
||||
{Action: ac.ActionDashboardsPermissionsRead, Scope: ac.ScopeFoldersAll},
|
||||
{Action: ac.ActionDashboardsPermissionsWrite, Scope: ac.ScopeFoldersAll},
|
||||
{Action: dashboards.ActionFoldersCreate},
|
||||
{Action: dashboards.ActionFoldersWrite, Scope: dashboards.ScopeFoldersAll},
|
||||
{Action: dashboards.ActionFoldersDelete, Scope: dashboards.ScopeFoldersAll},
|
||||
{Action: ac.ActionDashboardsWrite, Scope: dashboards.ScopeFoldersAll},
|
||||
{Action: ac.ActionDashboardsDelete, Scope: dashboards.ScopeFoldersAll},
|
||||
{Action: ac.ActionDashboardsCreate, Scope: dashboards.ScopeFoldersAll},
|
||||
{Action: ac.ActionDashboardsPermissionsRead, Scope: dashboards.ScopeFoldersAll},
|
||||
{Action: ac.ActionDashboardsPermissionsWrite, Scope: dashboards.ScopeFoldersAll},
|
||||
}),
|
||||
},
|
||||
Grants: []string{"Admin"},
|
||||
@@ -399,25 +389,25 @@ func (hs *HTTPServer) declareFixedRoles() error {
|
||||
|
||||
// dataSourcesConfigurationAccessEvaluator is used to protect the "Configure > Data sources" tab access
|
||||
var dataSourcesConfigurationAccessEvaluator = ac.EvalAll(
|
||||
ac.EvalPermission(ActionDatasourcesRead),
|
||||
ac.EvalPermission(datasources.ActionDatasourcesRead),
|
||||
ac.EvalAny(
|
||||
ac.EvalPermission(ActionDatasourcesCreate),
|
||||
ac.EvalPermission(ActionDatasourcesDelete),
|
||||
ac.EvalPermission(ActionDatasourcesWrite),
|
||||
ac.EvalPermission(datasources.ActionDatasourcesCreate),
|
||||
ac.EvalPermission(datasources.ActionDatasourcesDelete),
|
||||
ac.EvalPermission(datasources.ActionDatasourcesWrite),
|
||||
),
|
||||
)
|
||||
|
||||
// dataSourcesNewAccessEvaluator is used to protect the "Configure > Data sources > New" page access
|
||||
var dataSourcesNewAccessEvaluator = ac.EvalAll(
|
||||
ac.EvalPermission(ActionDatasourcesRead),
|
||||
ac.EvalPermission(ActionDatasourcesCreate),
|
||||
ac.EvalPermission(ActionDatasourcesWrite),
|
||||
ac.EvalPermission(datasources.ActionDatasourcesRead),
|
||||
ac.EvalPermission(datasources.ActionDatasourcesCreate),
|
||||
ac.EvalPermission(datasources.ActionDatasourcesWrite),
|
||||
)
|
||||
|
||||
// dataSourcesEditAccessEvaluator is used to protect the "Configure > Data sources > Edit" page access
|
||||
var dataSourcesEditAccessEvaluator = ac.EvalAll(
|
||||
ac.EvalPermission(ActionDatasourcesRead),
|
||||
ac.EvalPermission(ActionDatasourcesWrite),
|
||||
ac.EvalPermission(datasources.ActionDatasourcesRead),
|
||||
ac.EvalPermission(datasources.ActionDatasourcesWrite),
|
||||
)
|
||||
|
||||
// orgPreferencesAccessEvaluator is used to protect the "Configure > Preferences" page access
|
||||
|
||||
+28
-26
@@ -12,6 +12,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
acmiddleware "github.com/grafana/grafana/pkg/services/accesscontrol/middleware"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
)
|
||||
|
||||
@@ -273,18 +275,18 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
|
||||
// Data sources
|
||||
apiRoute.Group("/datasources", func(datasourceRoute routing.RouteRegister) {
|
||||
datasourceRoute.Get("/", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesRead)), routing.Wrap(hs.GetDataSources))
|
||||
datasourceRoute.Post("/", authorize(reqOrgAdmin, ac.EvalPermission(ActionDatasourcesCreate)), quota("data_source"), routing.Wrap(hs.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)), 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(hs.GetDataSourceByName))
|
||||
datasourceRoute.Get("/", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionDatasourcesRead)), routing.Wrap(hs.GetDataSources))
|
||||
datasourceRoute.Post("/", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionDatasourcesCreate)), quota("data_source"), routing.Wrap(hs.AddDataSource))
|
||||
datasourceRoute.Put("/:id", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionDatasourcesWrite, datasources.ScopeDatasourcesProvider.GetResourceScope(ac.Parameter(":id")))), routing.Wrap(hs.UpdateDataSource))
|
||||
datasourceRoute.Delete("/:id", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionDatasourcesDelete, datasources.ScopeDatasourcesProvider.GetResourceScope(ac.Parameter(":id")))), routing.Wrap(hs.DeleteDataSourceById))
|
||||
datasourceRoute.Delete("/uid/:uid", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionDatasourcesDelete, datasources.ScopeDatasourcesProvider.GetResourceScopeUID(ac.Parameter(":uid")))), routing.Wrap(hs.DeleteDataSourceByUID))
|
||||
datasourceRoute.Delete("/name/:name", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionDatasourcesDelete, datasources.ScopeDatasourcesProvider.GetResourceScopeName(ac.Parameter(":name")))), routing.Wrap(hs.DeleteDataSourceByName))
|
||||
datasourceRoute.Get("/:id", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionDatasourcesRead)), routing.Wrap(hs.GetDataSourceById))
|
||||
datasourceRoute.Get("/uid/:uid", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionDatasourcesRead)), routing.Wrap(hs.GetDataSourceByUID))
|
||||
datasourceRoute.Get("/name/:name", authorize(reqOrgAdmin, ac.EvalPermission(datasources.ActionDatasourcesRead)), routing.Wrap(hs.GetDataSourceByName))
|
||||
})
|
||||
|
||||
apiRoute.Get("/datasources/id/:name", authorize(reqSignedIn, ac.EvalPermission(ActionDatasourcesIDRead, ScopeDatasourceName)), routing.Wrap(hs.GetDataSourceIdByName))
|
||||
apiRoute.Get("/datasources/id/:name", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionDatasourcesIDRead, datasources.ScopeDatasourcesProvider.GetResourceScopeName(ac.Parameter(":name")))), routing.Wrap(hs.GetDataSourceIdByName))
|
||||
|
||||
apiRoute.Get("/plugins", routing.Wrap(hs.GetPluginList))
|
||||
apiRoute.Get("/plugins/:pluginId/settings", routing.Wrap(hs.GetPluginSettingByID))
|
||||
@@ -306,26 +308,26 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
}, reqOrgAdmin)
|
||||
|
||||
apiRoute.Get("/frontend/settings/", hs.GetFrontendSettings)
|
||||
apiRoute.Any("/datasources/proxy/:id/*", authorize(reqSignedIn, ac.EvalPermission(ActionDatasourcesQuery)), hs.ProxyDataSourceRequest)
|
||||
apiRoute.Any("/datasources/proxy/:id", authorize(reqSignedIn, ac.EvalPermission(ActionDatasourcesQuery)), hs.ProxyDataSourceRequest)
|
||||
apiRoute.Any("/datasources/:id/resources", authorize(reqSignedIn, ac.EvalPermission(ActionDatasourcesQuery)), hs.CallDatasourceResource)
|
||||
apiRoute.Any("/datasources/:id/resources/*", authorize(reqSignedIn, ac.EvalPermission(ActionDatasourcesQuery)), hs.CallDatasourceResource)
|
||||
apiRoute.Any("/datasources/:id/health", authorize(reqSignedIn, ac.EvalPermission(ActionDatasourcesQuery)), routing.Wrap(hs.CheckDatasourceHealth))
|
||||
apiRoute.Any("/datasources/proxy/:id/*", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionDatasourcesQuery)), hs.ProxyDataSourceRequest)
|
||||
apiRoute.Any("/datasources/proxy/:id", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionDatasourcesQuery)), hs.ProxyDataSourceRequest)
|
||||
apiRoute.Any("/datasources/:id/resources", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionDatasourcesQuery)), hs.CallDatasourceResource)
|
||||
apiRoute.Any("/datasources/:id/resources/*", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionDatasourcesQuery)), hs.CallDatasourceResource)
|
||||
apiRoute.Any("/datasources/:id/health", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionDatasourcesQuery)), routing.Wrap(hs.CheckDatasourceHealth))
|
||||
|
||||
// Folders
|
||||
apiRoute.Group("/folders", func(folderRoute routing.RouteRegister) {
|
||||
folderRoute.Get("/", authorize(reqSignedIn, ac.EvalPermission(ac.ActionFoldersRead)), routing.Wrap(hs.GetFolders))
|
||||
folderRoute.Get("/id/:id", authorize(reqSignedIn, ac.EvalPermission(ac.ActionFoldersRead, ac.ScopeFolderID)), routing.Wrap(hs.GetFolderByID))
|
||||
folderRoute.Post("/", authorize(reqSignedIn, ac.EvalPermission(ac.ActionFoldersCreate)), routing.Wrap(hs.CreateFolder))
|
||||
folderRoute.Get("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionFoldersRead)), routing.Wrap(hs.GetFolders))
|
||||
folderRoute.Get("/id/:id", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionFoldersRead, dashboards.ScopeFoldersProvider.GetResourceScope(ac.Parameter(":id")))), routing.Wrap(hs.GetFolderByID))
|
||||
folderRoute.Post("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionFoldersCreate)), routing.Wrap(hs.CreateFolder))
|
||||
|
||||
folderRoute.Group("/:uid", func(folderUidRoute routing.RouteRegister) {
|
||||
folderUidRoute.Get("/", authorize(reqSignedIn, ac.EvalPermission(ac.ActionFoldersRead)), routing.Wrap(hs.GetFolderByUID))
|
||||
folderUidRoute.Put("/", authorize(reqSignedIn, ac.EvalPermission(ac.ActionFoldersWrite)), routing.Wrap(hs.UpdateFolder))
|
||||
folderUidRoute.Delete("/", authorize(reqSignedIn, ac.EvalPermission(ac.ActionFoldersDelete)), routing.Wrap(hs.DeleteFolder))
|
||||
folderUidRoute.Get("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionFoldersRead)), routing.Wrap(hs.GetFolderByUID))
|
||||
folderUidRoute.Put("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionFoldersWrite)), routing.Wrap(hs.UpdateFolder))
|
||||
folderUidRoute.Delete("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionFoldersDelete)), routing.Wrap(hs.DeleteFolder))
|
||||
|
||||
folderUidRoute.Group("/permissions", func(folderPermissionRoute routing.RouteRegister) {
|
||||
folderPermissionRoute.Get("/", authorize(reqSignedIn, ac.EvalPermission(ac.ActionFoldersPermissionsRead)), routing.Wrap(hs.GetFolderPermissionList))
|
||||
folderPermissionRoute.Post("/", authorize(reqSignedIn, ac.EvalPermission(ac.ActionFoldersPermissionsWrite)), routing.Wrap(hs.UpdateFolderPermissions))
|
||||
folderPermissionRoute.Get("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionFoldersPermissionsRead)), routing.Wrap(hs.GetFolderPermissionList))
|
||||
folderPermissionRoute.Post("/", authorize(reqSignedIn, ac.EvalPermission(dashboards.ActionFoldersPermissionsWrite)), routing.Wrap(hs.UpdateFolderPermissions))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -384,13 +386,13 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
apiRoute.Get("/search/", routing.Wrap(hs.Search))
|
||||
|
||||
// metrics
|
||||
apiRoute.Post("/tsdb/query", authorize(reqSignedIn, ac.EvalPermission(ActionDatasourcesQuery)), routing.Wrap(hs.QueryMetrics))
|
||||
apiRoute.Post("/tsdb/query", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionDatasourcesQuery)), routing.Wrap(hs.QueryMetrics))
|
||||
|
||||
// DataSource w/ expressions
|
||||
apiRoute.Post("/ds/query", authorize(reqSignedIn, ac.EvalPermission(ActionDatasourcesQuery)), routing.Wrap(hs.QueryMetricsV2))
|
||||
apiRoute.Post("/ds/query", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionDatasourcesQuery)), routing.Wrap(hs.QueryMetricsV2))
|
||||
|
||||
// Validated query
|
||||
apiRoute.Post("/dashboards/org/:orgId/uid/:dashboardUid/panels/:panelId/query", authorize(reqSignedIn, ac.EvalPermission(ActionDatasourcesQuery)), routing.Wrap(hs.QueryMetricsFromDashboard))
|
||||
apiRoute.Post("/dashboards/org/:orgId/uid/:dashboardUid/panels/:panelId/query", authorize(reqSignedIn, ac.EvalPermission(datasources.ActionDatasourcesQuery)), routing.Wrap(hs.QueryMetricsFromDashboard))
|
||||
|
||||
apiRoute.Group("/alerts", func(alertsRoute routing.RouteRegister) {
|
||||
alertsRoute.Post("/test", routing.Wrap(hs.AlertTest))
|
||||
|
||||
+37
-36
@@ -10,16 +10,17 @@ import (
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/datasources"
|
||||
"github.com/grafana/grafana/pkg/services/datasources/permissions"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -238,10 +239,10 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesPut should return 404 if datasource not found",
|
||||
url: fmt.Sprintf("/api/datasources/%v", "12345678"),
|
||||
method: http.MethodPut,
|
||||
permissions: []*accesscontrol.Permission{
|
||||
permissions: []*ac.Permission{
|
||||
{
|
||||
Action: ActionDatasourcesWrite,
|
||||
Scope: ScopeDatasourcesAll,
|
||||
Action: datasources.ActionDatasourcesWrite,
|
||||
Scope: datasources.ScopeDatasourcesAll,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -253,7 +254,7 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesGet should return 200 for user with correct permissions",
|
||||
url: "/api/datasources/",
|
||||
method: http.MethodGet,
|
||||
permissions: []*accesscontrol.Permission{{Action: ActionDatasourcesRead, Scope: ScopeDatasourcesAll}},
|
||||
permissions: []*ac.Permission{{Action: datasources.ActionDatasourcesRead, Scope: datasources.ScopeDatasourcesAll}},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -262,7 +263,7 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesGet should return 403 for user without required permissions",
|
||||
url: "/api/datasources/",
|
||||
method: http.MethodGet,
|
||||
permissions: []*accesscontrol.Permission{{Action: "wrong"}},
|
||||
permissions: []*ac.Permission{{Action: "wrong"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -272,7 +273,7 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesPost should return 200 for user with correct permissions",
|
||||
url: "/api/datasources/",
|
||||
method: http.MethodPost,
|
||||
permissions: []*accesscontrol.Permission{{Action: ActionDatasourcesCreate}},
|
||||
permissions: []*ac.Permission{{Action: datasources.ActionDatasourcesCreate}},
|
||||
},
|
||||
expectedDS: &testDatasource,
|
||||
},
|
||||
@@ -282,7 +283,7 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesPost should return 403 for user without required permissions",
|
||||
url: "/api/datasources/",
|
||||
method: http.MethodPost,
|
||||
permissions: []*accesscontrol.Permission{{Action: "wrong"}},
|
||||
permissions: []*ac.Permission{{Action: "wrong"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -292,9 +293,9 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesPut should return 200 for user with correct permissions",
|
||||
url: fmt.Sprintf("/api/datasources/%v", testDatasource.Id),
|
||||
method: http.MethodPut,
|
||||
permissions: []*accesscontrol.Permission{
|
||||
permissions: []*ac.Permission{
|
||||
{
|
||||
Action: ActionDatasourcesWrite,
|
||||
Action: datasources.ActionDatasourcesWrite,
|
||||
Scope: fmt.Sprintf("datasources:id:%v", testDatasource.Id),
|
||||
},
|
||||
},
|
||||
@@ -307,7 +308,7 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesPut should return 403 for user without required permissions",
|
||||
url: fmt.Sprintf("/api/datasources/%v", testDatasource.Id),
|
||||
method: http.MethodPut,
|
||||
permissions: []*accesscontrol.Permission{{Action: "wrong"}},
|
||||
permissions: []*ac.Permission{{Action: "wrong"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -317,9 +318,9 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesPut should return 403 for read only datasource",
|
||||
url: fmt.Sprintf("/api/datasources/%v", testDatasourceReadOnly.Id),
|
||||
method: http.MethodPut,
|
||||
permissions: []*accesscontrol.Permission{
|
||||
permissions: []*ac.Permission{
|
||||
{
|
||||
Action: ActionDatasourcesWrite,
|
||||
Action: datasources.ActionDatasourcesWrite,
|
||||
Scope: fmt.Sprintf("datasources:id:%v", testDatasourceReadOnly.Id),
|
||||
},
|
||||
},
|
||||
@@ -332,9 +333,9 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesDeleteByID should return 200 for user with correct permissions",
|
||||
url: fmt.Sprintf("/api/datasources/%v", testDatasource.Id),
|
||||
method: http.MethodDelete,
|
||||
permissions: []*accesscontrol.Permission{
|
||||
permissions: []*ac.Permission{
|
||||
{
|
||||
Action: ActionDatasourcesDelete,
|
||||
Action: datasources.ActionDatasourcesDelete,
|
||||
Scope: fmt.Sprintf("datasources:id:%v", testDatasource.Id),
|
||||
},
|
||||
},
|
||||
@@ -347,7 +348,7 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesDeleteByID should return 403 for user without required permissions",
|
||||
url: fmt.Sprintf("/api/datasources/%v", testDatasource.Id),
|
||||
method: http.MethodDelete,
|
||||
permissions: []*accesscontrol.Permission{{Action: "wrong"}},
|
||||
permissions: []*ac.Permission{{Action: "wrong"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -356,9 +357,9 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesDeleteByUID should return 200 for user with correct permissions",
|
||||
url: fmt.Sprintf("/api/datasources/uid/%v", testDatasource.Uid),
|
||||
method: http.MethodDelete,
|
||||
permissions: []*accesscontrol.Permission{
|
||||
permissions: []*ac.Permission{
|
||||
{
|
||||
Action: ActionDatasourcesDelete,
|
||||
Action: datasources.ActionDatasourcesDelete,
|
||||
Scope: fmt.Sprintf("datasources:uid:%v", testDatasource.Uid),
|
||||
},
|
||||
},
|
||||
@@ -371,7 +372,7 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesDeleteByUID should return 403 for user without required permissions",
|
||||
url: fmt.Sprintf("/api/datasources/uid/%v", testDatasource.Uid),
|
||||
method: http.MethodDelete,
|
||||
permissions: []*accesscontrol.Permission{{Action: "wrong"}},
|
||||
permissions: []*ac.Permission{{Action: "wrong"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -380,9 +381,9 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesDeleteByName should return 200 for user with correct permissions",
|
||||
url: fmt.Sprintf("/api/datasources/name/%v", testDatasource.Name),
|
||||
method: http.MethodDelete,
|
||||
permissions: []*accesscontrol.Permission{
|
||||
permissions: []*ac.Permission{
|
||||
{
|
||||
Action: ActionDatasourcesDelete,
|
||||
Action: datasources.ActionDatasourcesDelete,
|
||||
Scope: fmt.Sprintf("datasources:name:%v", testDatasource.Name),
|
||||
},
|
||||
},
|
||||
@@ -395,7 +396,7 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesDeleteByName should return 403 for user without required permissions",
|
||||
url: fmt.Sprintf("/api/datasources/name/%v", testDatasource.Name),
|
||||
method: http.MethodDelete,
|
||||
permissions: []*accesscontrol.Permission{{Action: "wrong"}},
|
||||
permissions: []*ac.Permission{{Action: "wrong"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -404,9 +405,9 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesGetByID should return 200 for user with correct permissions",
|
||||
url: fmt.Sprintf("/api/datasources/%v", testDatasource.Id),
|
||||
method: http.MethodGet,
|
||||
permissions: []*accesscontrol.Permission{
|
||||
permissions: []*ac.Permission{
|
||||
{
|
||||
Action: ActionDatasourcesRead,
|
||||
Action: datasources.ActionDatasourcesRead,
|
||||
Scope: fmt.Sprintf("datasources:id:%v", testDatasource.Id),
|
||||
},
|
||||
},
|
||||
@@ -419,7 +420,7 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesGetByID should return 403 for user without required permissions",
|
||||
url: fmt.Sprintf("/api/datasources/%v", testDatasource.Id),
|
||||
method: http.MethodGet,
|
||||
permissions: []*accesscontrol.Permission{{Action: "wrong"}},
|
||||
permissions: []*ac.Permission{{Action: "wrong"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -428,9 +429,9 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesGetByUID should return 200 for user with correct permissions",
|
||||
url: fmt.Sprintf("/api/datasources/uid/%v", testDatasource.Uid),
|
||||
method: http.MethodGet,
|
||||
permissions: []*accesscontrol.Permission{
|
||||
permissions: []*ac.Permission{
|
||||
{
|
||||
Action: ActionDatasourcesRead,
|
||||
Action: datasources.ActionDatasourcesRead,
|
||||
Scope: fmt.Sprintf("datasources:uid:%v", testDatasource.Uid),
|
||||
},
|
||||
},
|
||||
@@ -443,7 +444,7 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesGetByUID should return 403 for user without required permissions",
|
||||
url: fmt.Sprintf("/api/datasources/uid/%v", testDatasource.Uid),
|
||||
method: http.MethodGet,
|
||||
permissions: []*accesscontrol.Permission{{Action: "wrong"}},
|
||||
permissions: []*ac.Permission{{Action: "wrong"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -452,9 +453,9 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesGetByName should return 200 for user with correct permissions",
|
||||
url: fmt.Sprintf("/api/datasources/name/%v", testDatasource.Name),
|
||||
method: http.MethodGet,
|
||||
permissions: []*accesscontrol.Permission{
|
||||
permissions: []*ac.Permission{
|
||||
{
|
||||
Action: ActionDatasourcesRead,
|
||||
Action: datasources.ActionDatasourcesRead,
|
||||
Scope: fmt.Sprintf("datasources:name:%v", testDatasource.Name),
|
||||
},
|
||||
},
|
||||
@@ -467,7 +468,7 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesGetByName should return 403 for user without required permissions",
|
||||
url: fmt.Sprintf("/api/datasources/name/%v", testDatasource.Name),
|
||||
method: http.MethodGet,
|
||||
permissions: []*accesscontrol.Permission{{Action: "wrong"}},
|
||||
permissions: []*ac.Permission{{Action: "wrong"}},
|
||||
},
|
||||
expectedDS: &testDatasource,
|
||||
},
|
||||
@@ -477,9 +478,9 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesGetIdByName should return 200 for user with correct permissions",
|
||||
url: fmt.Sprintf("/api/datasources/id/%v", testDatasource.Name),
|
||||
method: http.MethodGet,
|
||||
permissions: []*accesscontrol.Permission{
|
||||
permissions: []*ac.Permission{
|
||||
{
|
||||
Action: ActionDatasourcesIDRead,
|
||||
Action: datasources.ActionDatasourcesIDRead,
|
||||
Scope: fmt.Sprintf("datasources:name:%v", testDatasource.Name),
|
||||
},
|
||||
},
|
||||
@@ -492,7 +493,7 @@ func TestAPI_Datasources_AccessControl(t *testing.T) {
|
||||
desc: "DatasourcesGetIdByName should return 403 for user without required permissions",
|
||||
url: fmt.Sprintf("/api/datasources/id/%v", testDatasource.Name),
|
||||
method: http.MethodGet,
|
||||
permissions: []*accesscontrol.Permission{{Action: "wrong"}},
|
||||
permissions: []*ac.Permission{{Action: "wrong"}},
|
||||
},
|
||||
expectedDS: &testDatasource,
|
||||
},
|
||||
|
||||
+3
-2
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
@@ -480,7 +481,7 @@ func (hs *HTTPServer) buildCreateNavLinks(c *models.ReqContext) []*dtos.NavLink
|
||||
children = append(children, &dtos.NavLink{Text: "Dashboard", Icon: "apps", Url: hs.Cfg.AppSubURL + "/dashboard/new", Id: "create-dashboard"})
|
||||
}
|
||||
|
||||
if hasAccess(ac.ReqOrgAdminOrEditor, ac.EvalPermission(ac.ActionFoldersCreate)) {
|
||||
if hasAccess(ac.ReqOrgAdminOrEditor, ac.EvalPermission(dashboards.ActionFoldersCreate)) {
|
||||
children = append(children, &dtos.NavLink{
|
||||
Text: "Folder", SubTitle: "Create a new folder to organize your dashboards", Id: "folder",
|
||||
Icon: "folder-plus", Url: hs.Cfg.AppSubURL + "/dashboards/folder/new",
|
||||
@@ -553,7 +554,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
|
||||
return false
|
||||
}
|
||||
return hasEditPermissionInFoldersQuery.Result
|
||||
}, ac.EvalAny(ac.EvalPermission(ac.ActionDashboardsCreate), ac.EvalPermission(ac.ActionFoldersCreate)))
|
||||
}, ac.EvalAny(ac.EvalPermission(ac.ActionDashboardsCreate), ac.EvalPermission(dashboards.ActionFoldersCreate)))
|
||||
|
||||
settings, err := hs.getFrontendSettingsMap(c)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user