diff --git a/pkg/api/accesscontrol.go b/pkg/api/accesscontrol.go index 0c2a5748f84..5b0d2cd2b55 100644 --- a/pkg/api/accesscontrol.go +++ b/pkg/api/accesscontrol.go @@ -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 diff --git a/pkg/api/api.go b/pkg/api/api.go index 37d1d1fb421..6158e0f0933 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -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)) diff --git a/pkg/api/datasources_test.go b/pkg/api/datasources_test.go index 7f7191e3428..ceea37caeb5 100644 --- a/pkg/api/datasources_test.go +++ b/pkg/api/datasources_test.go @@ -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, }, diff --git a/pkg/api/index.go b/pkg/api/index.go index 50b13d23741..fbc8ecffb6d 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -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 { diff --git a/pkg/services/accesscontrol/models.go b/pkg/services/accesscontrol/models.go index b00836ec4e1..9e186528376 100644 --- a/pkg/services/accesscontrol/models.go +++ b/pkg/services/accesscontrol/models.go @@ -346,17 +346,6 @@ const ( // Dashboard scopes ScopeDashboardsAll = "dashboards:*" - - // Folder actions - ActionFoldersCreate = "folders:create" - ActionFoldersRead = "folders:read" - ActionFoldersWrite = "folders:write" - ActionFoldersDelete = "folders:delete" - ActionFoldersPermissionsRead = "folders.permissions:read" - ActionFoldersPermissionsWrite = "folders.permissions:write" - - // Folder scopes - ScopeFoldersAll = "folders:*" ) var ( @@ -364,7 +353,9 @@ var ( ScopeTeamsID = Scope("teams", "id", Parameter(":teamId")) // Folder scopes - ScopeFolderID = Scope("folders", "id", Parameter(":id")) + + // Datasource scopes + ) const RoleGrafanaAdmin = "Grafana Admin" diff --git a/pkg/services/accesscontrol/ossaccesscontrol/permissions_services.go b/pkg/services/accesscontrol/ossaccesscontrol/permissions_services.go index aad388311b0..db7576d61fc 100644 --- a/pkg/services/accesscontrol/ossaccesscontrol/permissions_services.go +++ b/pkg/services/accesscontrol/ossaccesscontrol/permissions_services.go @@ -10,6 +10,7 @@ import ( "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/accesscontrol" "github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions" + "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/setting" ) @@ -139,9 +140,9 @@ func ProvideTeamPermissions( var DashboardViewActions = []string{accesscontrol.ActionDashboardsRead} var DashboardEditActions = append(DashboardViewActions, []string{accesscontrol.ActionDashboardsWrite, accesscontrol.ActionDashboardsDelete}...) var DashboardAdminActions = append(DashboardEditActions, []string{accesscontrol.ActionDashboardsPermissionsRead, accesscontrol.ActionDashboardsPermissionsWrite}...) -var FolderViewActions = []string{accesscontrol.ActionFoldersRead} -var FolderEditActions = append(FolderViewActions, []string{accesscontrol.ActionFoldersWrite, accesscontrol.ActionFoldersDelete, accesscontrol.ActionDashboardsCreate}...) -var FolderAdminActions = append(FolderEditActions, []string{accesscontrol.ActionFoldersPermissionsRead, accesscontrol.ActionFoldersPermissionsWrite}...) +var FolderViewActions = []string{dashboards.ActionFoldersRead} +var FolderEditActions = append(FolderViewActions, []string{dashboards.ActionFoldersWrite, dashboards.ActionFoldersDelete, accesscontrol.ActionDashboardsCreate}...) +var FolderAdminActions = append(FolderEditActions, []string{dashboards.ActionFoldersPermissionsRead, dashboards.ActionFoldersPermissionsWrite}...) func provideDashboardService( cfg *setting.Cfg, router routing.RouteRegister, sql *sqlstore.SQLStore, diff --git a/pkg/services/accesscontrol/scope.go b/pkg/services/accesscontrol/scope.go index eb8c5476c7b..326100d2d67 100644 --- a/pkg/services/accesscontrol/scope.go +++ b/pkg/services/accesscontrol/scope.go @@ -22,6 +22,14 @@ func GetResourceScope(resource string, resourceID string) string { return Scope(resource, "id", resourceID) } +func GetResourceScopeUID(resource string, resourceID string) string { + return Scope(resource, "uid", resourceID) +} + +func GetResourceScopeName(resource string, resourceID string) string { + return Scope(resource, "name", resourceID) +} + func GetResourceAllScope(resource string) string { return Scope(resource, "*") } @@ -165,3 +173,48 @@ func ScopeInjector(params ScopeParams) ScopeMutator { return buf.String(), nil } } + +// ScopeProvider provides methods that construct scopes +type ScopeProvider interface { + GetResourceScope(resourceID string) string + GetResourceScopeUID(resourceID string) string + GetResourceScopeName(resourceID string) string + GetResourceAllScope() string + GetResourceAllIDScope() string +} + +type scopeProviderImpl struct { + root string +} + +// NewScopeProvider creates a new ScopeProvider that is configured with specific root scope +func NewScopeProvider(root string) ScopeProvider { + return &scopeProviderImpl{ + root: root, + } +} + +// GetResourceScope returns scope that has the format ":id:" +func (s scopeProviderImpl) GetResourceScope(resourceID string) string { + return GetResourceScope(s.root, resourceID) +} + +// GetResourceScopeUID returns scope that has the format ":uid:" +func (s scopeProviderImpl) GetResourceScopeUID(resourceID string) string { + return GetResourceScopeUID(s.root, resourceID) +} + +// GetResourceScopeName returns scope that has the format ":name:" +func (s scopeProviderImpl) GetResourceScopeName(resourceID string) string { + return GetResourceScopeName(s.root, resourceID) +} + +// GetResourceAllScope returns scope that has the format ":*" +func (s scopeProviderImpl) GetResourceAllScope() string { + return GetResourceAllScope(s.root) +} + +// GetResourceAllIDScope returns scope that has the format ":id:*" +func (s scopeProviderImpl) GetResourceAllIDScope() string { + return GetResourceAllIDScope(s.root) +} diff --git a/pkg/services/dashboards/accesscontrol.go b/pkg/services/dashboards/accesscontrol.go new file mode 100644 index 00000000000..005ce21d668 --- /dev/null +++ b/pkg/services/dashboards/accesscontrol.go @@ -0,0 +1,19 @@ +package dashboards + +import "github.com/grafana/grafana/pkg/services/accesscontrol" + +const ( + ActionFoldersCreate = "folders:create" + ActionFoldersRead = "folders:read" + ActionFoldersWrite = "folders:write" + ActionFoldersDelete = "folders:delete" + ActionFoldersPermissionsRead = "folders.permissions:read" + ActionFoldersPermissionsWrite = "folders.permissions:write" + + ScopeFoldersRoot = "folders" +) + +var ( + ScopeFoldersAll = accesscontrol.GetResourceAllScope(ScopeFoldersRoot) + ScopeFoldersProvider = accesscontrol.NewScopeProvider(ScopeFoldersRoot) +) diff --git a/pkg/services/dashboards/manager/dashboard_service.go b/pkg/services/dashboards/manager/dashboard_service.go index 3b1dacfed3e..e4a3d380250 100644 --- a/pkg/services/dashboards/manager/dashboard_service.go +++ b/pkg/services/dashboards/manager/dashboard_service.go @@ -7,6 +7,7 @@ import ( "time" "github.com/grafana/grafana-plugin-sdk-go/backend/gtime" + "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models" @@ -21,10 +22,10 @@ import ( var ( provisionerPermissions = map[string][]string{ - accesscontrol.ActionFoldersCreate: {}, - accesscontrol.ActionFoldersWrite: {accesscontrol.ScopeFoldersAll}, - accesscontrol.ActionDashboardsCreate: {accesscontrol.ScopeFoldersAll}, - accesscontrol.ActionDashboardsWrite: {accesscontrol.ScopeFoldersAll}, + m.ActionFoldersCreate: {}, + m.ActionFoldersWrite: {m.ScopeFoldersAll}, + accesscontrol.ActionDashboardsCreate: {m.ScopeFoldersAll}, + accesscontrol.ActionDashboardsWrite: {m.ScopeFoldersAll}, } ) diff --git a/pkg/services/datasources/accesscontrol.go b/pkg/services/datasources/accesscontrol.go new file mode 100644 index 00000000000..2f4e9a96b8d --- /dev/null +++ b/pkg/services/datasources/accesscontrol.go @@ -0,0 +1,19 @@ +package datasources + +import "github.com/grafana/grafana/pkg/services/accesscontrol" + +const ( + ActionDatasourcesRead = "datasources:read" + ActionDatasourcesQuery = "datasources:query" + ActionDatasourcesCreate = "datasources:create" + ActionDatasourcesWrite = "datasources:write" + ActionDatasourcesDelete = "datasources:delete" + ActionDatasourcesIDRead = "datasources.id:read" + + ScopeDatasourcesRoot = "datasources" +) + +var ( + ScopeDatasourcesAll = accesscontrol.GetResourceAllScope(ScopeDatasourcesRoot) + ScopeDatasourcesProvider = accesscontrol.NewScopeProvider(ScopeDatasourcesRoot) +) diff --git a/pkg/services/datasources/service/datasource_service.go b/pkg/services/datasources/service/datasource_service.go index 9f5e1cd273b..a1b82b14db5 100644 --- a/pkg/services/datasources/service/datasource_service.go +++ b/pkg/services/datasources/service/datasource_service.go @@ -11,11 +11,13 @@ import ( "time" sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" + "github.com/grafana/grafana/pkg/bus" "github.com/grafana/grafana/pkg/components/simplejson" "github.com/grafana/grafana/pkg/infra/httpclient" "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/datasources" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/secrets" "github.com/grafana/grafana/pkg/services/sqlstore" @@ -96,14 +98,14 @@ type DataSourceRetriever interface { func NewNameScopeResolver(db DataSourceRetriever) (string, accesscontrol.AttributeScopeResolveFunc) { dsNameResolver := func(ctx context.Context, orgID int64, initialScope string) (string, error) { dsNames := strings.Split(initialScope, ":") - if dsNames[0] != "datasources" || len(dsNames) != 3 { + if dsNames[0] != datasources.ScopeDatasourcesRoot || len(dsNames) != 3 { return "", accesscontrol.ErrInvalidScope } dsName := dsNames[2] // Special wildcard case if dsName == "*" { - return accesscontrol.Scope("datasources", "id", "*"), nil + return datasources.ScopeDatasourcesProvider.GetResourceAllIDScope(), nil } query := models.GetDataSourceQuery{Name: dsName, OrgId: orgID} @@ -111,10 +113,10 @@ func NewNameScopeResolver(db DataSourceRetriever) (string, accesscontrol.Attribu return "", err } - return accesscontrol.Scope("datasources", "id", fmt.Sprintf("%v", query.Result.Id)), nil + return datasources.ScopeDatasourcesProvider.GetResourceScope(fmt.Sprintf("%v", query.Result.Id)), nil } - return "datasources:name:", dsNameResolver + return datasources.ScopeDatasourcesProvider.GetResourceScopeName(""), dsNameResolver } func (s *Service) GetDataSource(ctx context.Context, query *models.GetDataSourceQuery) error { diff --git a/pkg/services/guardian/accesscontrol_guardian.go b/pkg/services/guardian/accesscontrol_guardian.go index c68217718a7..e13f53e8d7e 100644 --- a/pkg/services/guardian/accesscontrol_guardian.go +++ b/pkg/services/guardian/accesscontrol_guardian.go @@ -6,6 +6,7 @@ import ( "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/setting" ) @@ -48,7 +49,7 @@ func (a *AccessControlDashboardGuardian) CanSave() (bool, error) { } if a.dashboard.IsFolder { - return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalPermission(accesscontrol.ActionFoldersWrite, folderScope(a.dashboardID))) + return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalPermission(dashboards.ActionFoldersWrite, folderScope(a.dashboardID))) } return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalAny( @@ -67,7 +68,7 @@ func (a *AccessControlDashboardGuardian) CanEdit() (bool, error) { } if a.dashboard.IsFolder { - return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalPermission(accesscontrol.ActionFoldersWrite, folderScope(a.dashboardID))) + return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalPermission(dashboards.ActionFoldersWrite, folderScope(a.dashboardID))) } return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalAny( @@ -82,7 +83,7 @@ func (a *AccessControlDashboardGuardian) CanView() (bool, error) { } if a.dashboard.IsFolder { - return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalPermission(accesscontrol.ActionFoldersRead, folderScope(a.dashboardID))) + return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalPermission(dashboards.ActionFoldersRead, folderScope(a.dashboardID))) } return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalAny( @@ -98,8 +99,8 @@ func (a *AccessControlDashboardGuardian) CanAdmin() (bool, error) { if a.dashboard.IsFolder { return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalAll( - accesscontrol.EvalPermission(accesscontrol.ActionFoldersPermissionsRead, folderScope(a.dashboard.Id)), - accesscontrol.EvalPermission(accesscontrol.ActionFoldersPermissionsWrite, folderScope(a.dashboard.Id)), + accesscontrol.EvalPermission(dashboards.ActionFoldersPermissionsRead, folderScope(a.dashboard.Id)), + accesscontrol.EvalPermission(dashboards.ActionFoldersPermissionsWrite, folderScope(a.dashboard.Id)), )) } @@ -121,7 +122,7 @@ func (a *AccessControlDashboardGuardian) CanDelete() (bool, error) { } if a.dashboard.IsFolder { - return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalPermission(accesscontrol.ActionFoldersDelete, folderScope(a.dashboard.Id))) + return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalPermission(dashboards.ActionFoldersDelete, folderScope(a.dashboard.Id))) } return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalAny( @@ -132,7 +133,7 @@ func (a *AccessControlDashboardGuardian) CanDelete() (bool, error) { func (a *AccessControlDashboardGuardian) CanCreate(folderID int64, isFolder bool) (bool, error) { if isFolder { - return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalPermission(accesscontrol.ActionFoldersCreate)) + return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalPermission(dashboards.ActionFoldersCreate)) } return a.ac.Evaluate(a.ctx, a.user, accesscontrol.EvalPermission(accesscontrol.ActionDashboardsCreate, folderScope(folderID))) @@ -251,5 +252,5 @@ func dashboardScope(dashboardID int64) string { } func folderScope(folderID int64) string { - return accesscontrol.Scope("folders", "id", strconv.FormatInt(folderID, 10)) + return dashboards.ScopeFoldersProvider.GetResourceScope(strconv.FormatInt(folderID, 10)) } diff --git a/pkg/services/guardian/accesscontrol_guardian_test.go b/pkg/services/guardian/accesscontrol_guardian_test.go index 6399e1cbd65..8b439103adf 100644 --- a/pkg/services/guardian/accesscontrol_guardian_test.go +++ b/pkg/services/guardian/accesscontrol_guardian_test.go @@ -17,6 +17,7 @@ import ( "github.com/grafana/grafana/pkg/services/accesscontrol/database" accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock" "github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol" + "github.com/grafana/grafana/pkg/services/dashboards" dashdb "github.com/grafana/grafana/pkg/services/dashboards/database" "github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/setting" @@ -516,7 +517,7 @@ func TestAccessControlDashboardGuardian_CanCreate(t *testing.T) { isFolder: true, folderID: 0, permissions: []*accesscontrol.Permission{ - {Action: accesscontrol.ActionFoldersCreate}, + {Action: dashboards.ActionFoldersCreate}, }, expected: true, }, diff --git a/pkg/services/sqlstore/dashboard.go b/pkg/services/sqlstore/dashboard.go index b9a79a8af1c..ddbf3b94541 100644 --- a/pkg/services/sqlstore/dashboard.go +++ b/pkg/services/sqlstore/dashboard.go @@ -11,6 +11,7 @@ import ( "github.com/grafana/grafana/pkg/bus" "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/search" "github.com/grafana/grafana/pkg/services/sqlstore/permissions" "github.com/grafana/grafana/pkg/services/sqlstore/searchstore" @@ -280,7 +281,7 @@ func deleteDashboard(cmd *models.DeleteDashboardCommand, sess *DBSession) error } // remove all access control permission with folder scope - _, err = sess.Exec("DELETE FROM permission WHERE scope = ?", ac.Scope("folders", "id", strconv.FormatInt(dashboard.Id, 10))) + _, err = sess.Exec("DELETE FROM permission WHERE scope = ?", dashboards.ScopeFoldersProvider.GetResourceScope(strconv.FormatInt(dashboard.Id, 10))) if err != nil { return err } diff --git a/pkg/services/sqlstore/migrations/accesscontrol/dashboard_permissions.go b/pkg/services/sqlstore/migrations/accesscontrol/dashboard_permissions.go index 378ee381785..d909596bc78 100644 --- a/pkg/services/sqlstore/migrations/accesscontrol/dashboard_permissions.go +++ b/pkg/services/sqlstore/migrations/accesscontrol/dashboard_permissions.go @@ -10,6 +10,7 @@ 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/sqlstore/migrator" ) @@ -35,21 +36,21 @@ var dashboardPermissionTranslation = map[models.PermissionType][]string{ var folderPermissionTranslation = map[models.PermissionType][]string{ models.PERMISSION_VIEW: append(dashboardPermissionTranslation[models.PERMISSION_VIEW], []string{ - ac.ActionFoldersRead, + dashboards.ActionFoldersRead, }...), models.PERMISSION_EDIT: append(dashboardPermissionTranslation[models.PERMISSION_EDIT], []string{ - ac.ActionFoldersRead, - ac.ActionFoldersWrite, - ac.ActionFoldersCreate, - ac.ActionFoldersDelete, + dashboards.ActionFoldersRead, + dashboards.ActionFoldersWrite, + dashboards.ActionFoldersCreate, + dashboards.ActionFoldersDelete, }...), models.PERMISSION_ADMIN: append(dashboardPermissionTranslation[models.PERMISSION_ADMIN], []string{ - ac.ActionFoldersRead, - ac.ActionFoldersWrite, - ac.ActionFoldersCreate, - ac.ActionFoldersDelete, - ac.ActionFoldersPermissionsRead, - ac.ActionFoldersPermissionsWrite, + dashboards.ActionFoldersRead, + dashboards.ActionFoldersWrite, + dashboards.ActionFoldersCreate, + dashboards.ActionFoldersDelete, + dashboards.ActionFoldersPermissionsRead, + dashboards.ActionFoldersPermissionsWrite, }...), } @@ -202,7 +203,7 @@ func (m dashboardPermissionsMigrator) setPermissions(allRoles []*ac.Role, permis func (m dashboardPermissionsMigrator) mapPermission(id int64, p models.PermissionType, isFolder bool) []*ac.Permission { if isFolder { actions := folderPermissionTranslation[p] - scope := ac.Scope("folders", "id", strconv.FormatInt(id, 10)) + scope := dashboards.ScopeFoldersProvider.GetResourceScope(strconv.FormatInt(id, 10)) permissions := make([]*ac.Permission, 0, len(actions)) for _, action := range actions { permissions = append(permissions, &ac.Permission{Action: action, Scope: scope}) diff --git a/pkg/services/sqlstore/permissions/dashboard.go b/pkg/services/sqlstore/permissions/dashboard.go index a96f66d90f7..0b511bfc888 100644 --- a/pkg/services/sqlstore/permissions/dashboard.go +++ b/pkg/services/sqlstore/permissions/dashboard.go @@ -6,6 +6,7 @@ import ( "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/accesscontrol" + "github.com/grafana/grafana/pkg/services/dashboards" "github.com/grafana/grafana/pkg/services/sqlstore/migrator" ) @@ -82,7 +83,7 @@ type AccessControlDashboardPermissionFilter struct { } func (f AccessControlDashboardPermissionFilter) Where() (string, []interface{}) { - folderAction := accesscontrol.ActionFoldersRead + folderAction := dashboards.ActionFoldersRead dashboardAction := accesscontrol.ActionDashboardsRead if f.PermissionLevel == models.PERMISSION_EDIT { folderAction = accesscontrol.ActionDashboardsCreate