mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 "<rootScope>:id:<resourceID>"
|
||||
func (s scopeProviderImpl) GetResourceScope(resourceID string) string {
|
||||
return GetResourceScope(s.root, resourceID)
|
||||
}
|
||||
|
||||
// GetResourceScopeUID returns scope that has the format "<rootScope>:uid:<resourceID>"
|
||||
func (s scopeProviderImpl) GetResourceScopeUID(resourceID string) string {
|
||||
return GetResourceScopeUID(s.root, resourceID)
|
||||
}
|
||||
|
||||
// GetResourceScopeName returns scope that has the format "<rootScope>:name:<resourceID>"
|
||||
func (s scopeProviderImpl) GetResourceScopeName(resourceID string) string {
|
||||
return GetResourceScopeName(s.root, resourceID)
|
||||
}
|
||||
|
||||
// GetResourceAllScope returns scope that has the format "<rootScope>:*"
|
||||
func (s scopeProviderImpl) GetResourceAllScope() string {
|
||||
return GetResourceAllScope(s.root)
|
||||
}
|
||||
|
||||
// GetResourceAllIDScope returns scope that has the format "<rootScope>:id:*"
|
||||
func (s scopeProviderImpl) GetResourceAllIDScope() string {
|
||||
return GetResourceAllIDScope(s.root)
|
||||
}
|
||||
|
||||
19
pkg/services/dashboards/accesscontrol.go
Normal file
19
pkg/services/dashboards/accesscontrol.go
Normal file
@@ -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)
|
||||
)
|
||||
@@ -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},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
19
pkg/services/datasources/accesscontrol.go
Normal file
19
pkg/services/datasources/accesscontrol.go
Normal file
@@ -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)
|
||||
)
|
||||
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user