2022-03-09 10:57:50 -06:00
package dashboards
2022-03-10 11:19:50 -06:00
import (
"context"
"strings"
ac "github.com/grafana/grafana/pkg/services/accesscontrol"
2023-01-26 02:21:10 -06:00
"github.com/grafana/grafana/pkg/services/folder"
2022-03-10 11:19:50 -06:00
)
2022-03-09 10:57:50 -06:00
const (
2022-03-30 08:14:26 -05:00
ScopeFoldersRoot = "folders"
ScopeFoldersPrefix = "folders:uid:"
2022-03-09 10:57:50 -06:00
ActionFoldersCreate = "folders:create"
ActionFoldersRead = "folders:read"
ActionFoldersWrite = "folders:write"
ActionFoldersDelete = "folders:delete"
ActionFoldersPermissionsRead = "folders.permissions:read"
ActionFoldersPermissionsWrite = "folders.permissions:write"
2022-03-30 08:14:26 -05:00
ScopeDashboardsRoot = "dashboards"
ScopeDashboardsPrefix = "dashboards:uid:"
2022-05-04 09:12:09 -05:00
ActionDashboardsCreate = "dashboards:create"
ActionDashboardsRead = "dashboards:read"
ActionDashboardsWrite = "dashboards:write"
ActionDashboardsDelete = "dashboards:delete"
ActionDashboardsPermissionsRead = "dashboards.permissions:read"
ActionDashboardsPermissionsWrite = "dashboards.permissions:write"
2022-09-07 16:29:01 -05:00
ActionDashboardsPublicWrite = "dashboards.public:write"
2022-03-09 10:57:50 -06:00
)
var (
2022-05-04 09:12:09 -05:00
ScopeFoldersProvider = ac . NewScopeProvider ( ScopeFoldersRoot )
ScopeFoldersAll = ScopeFoldersProvider . GetResourceAllScope ( )
ScopeDashboardsProvider = ac . NewScopeProvider ( ScopeDashboardsRoot )
ScopeDashboardsAll = ScopeDashboardsProvider . GetResourceAllScope ( )
2022-03-09 10:57:50 -06:00
)
2022-03-10 11:19:50 -06:00
2022-05-02 02:29:30 -05:00
// NewFolderNameScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:name:" into an uid based scope.
2023-03-03 09:56:33 -06:00
func NewFolderNameScopeResolver ( folderDB folder . FolderStore , folderSvc folder . Service ) ( string , ac . ScopeAttributeResolver ) {
2022-03-10 11:19:50 -06:00
prefix := ScopeFoldersProvider . GetResourceScopeName ( "" )
2022-05-02 02:29:30 -05:00
return prefix , ac . ScopeAttributeResolverFunc ( func ( ctx context . Context , orgID int64 , scope string ) ( [ ] string , error ) {
2022-03-10 11:19:50 -06:00
if ! strings . HasPrefix ( scope , prefix ) {
2022-05-02 02:29:30 -05:00
return nil , ac . ErrInvalidScope
2022-03-10 11:19:50 -06:00
}
nsName := scope [ len ( prefix ) : ]
if len ( nsName ) == 0 {
2022-05-02 02:29:30 -05:00
return nil , ac . ErrInvalidScope
2022-03-10 11:19:50 -06:00
}
2023-03-03 09:56:33 -06:00
folder , err := folderDB . GetFolderByTitle ( ctx , orgID , nsName )
2022-03-10 11:19:50 -06:00
if err != nil {
2022-05-02 02:29:30 -05:00
return nil , err
2022-03-10 11:19:50 -06:00
}
2023-01-26 02:21:10 -06:00
2023-01-30 08:19:42 -06:00
result , err := GetInheritedScopes ( ctx , folder . OrgID , folder . UID , folderSvc )
2023-01-26 02:21:10 -06:00
if err != nil {
return nil , err
}
result = append ( [ ] string { ScopeFoldersProvider . GetResourceScopeUID ( folder . UID ) } , result ... )
return result , nil
2022-05-02 02:29:30 -05:00
} )
2022-03-10 11:19:50 -06:00
}
2022-03-15 09:37:16 -05:00
2022-05-02 02:29:30 -05:00
// NewFolderIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:id:" into an uid based scope.
2023-03-03 09:56:33 -06:00
func NewFolderIDScopeResolver ( folderDB folder . FolderStore , folderSvc folder . Service ) ( string , ac . ScopeAttributeResolver ) {
2022-03-30 08:14:26 -05:00
prefix := ScopeFoldersProvider . GetResourceScope ( "" )
2022-05-02 02:29:30 -05:00
return prefix , ac . ScopeAttributeResolverFunc ( func ( ctx context . Context , orgID int64 , scope string ) ( [ ] string , error ) {
2022-03-15 09:37:16 -05:00
if ! strings . HasPrefix ( scope , prefix ) {
2022-05-02 02:29:30 -05:00
return nil , ac . ErrInvalidScope
2022-03-15 09:37:16 -05:00
}
2022-03-30 08:14:26 -05:00
2022-06-07 04:02:20 -05:00
id , err := ac . ParseScopeID ( scope )
2022-03-30 08:14:26 -05:00
if err != nil {
2022-06-07 04:02:20 -05:00
return nil , err
2022-03-15 09:37:16 -05:00
}
2022-03-30 08:14:26 -05:00
if id == 0 {
2022-05-02 02:29:30 -05:00
return [ ] string { ScopeFoldersProvider . GetResourceScopeUID ( ac . GeneralFolderUID ) } , nil
2022-03-30 08:14:26 -05:00
}
2023-03-03 09:56:33 -06:00
folder , err := folderDB . GetFolderByID ( ctx , orgID , id )
2022-03-15 09:37:16 -05:00
if err != nil {
2022-05-02 02:29:30 -05:00
return nil , err
2022-03-15 09:37:16 -05:00
}
2022-03-30 08:14:26 -05:00
2023-01-30 08:19:42 -06:00
result , err := GetInheritedScopes ( ctx , folder . OrgID , folder . UID , folderSvc )
2023-01-26 02:21:10 -06:00
if err != nil {
return nil , err
}
result = append ( [ ] string { ScopeFoldersProvider . GetResourceScopeUID ( folder . UID ) } , result ... )
return result , nil
2022-05-02 02:29:30 -05:00
} )
2022-03-15 09:37:16 -05:00
}
2023-02-07 10:27:20 -06:00
// NewFolderUIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "folders:uid:"
// into uid based scopes for folder and its parents
2023-03-02 07:09:57 -06:00
func NewFolderUIDScopeResolver ( folderSvc folder . Service ) ( string , ac . ScopeAttributeResolver ) {
2023-02-07 10:27:20 -06:00
prefix := ScopeFoldersProvider . GetResourceScopeUID ( "" )
return prefix , ac . ScopeAttributeResolverFunc ( func ( ctx context . Context , orgID int64 , scope string ) ( [ ] string , error ) {
if ! strings . HasPrefix ( scope , prefix ) {
return nil , ac . ErrInvalidScope
}
uid , err := ac . ParseScopeUID ( scope )
if err != nil {
return nil , err
}
inheritedScopes , err := GetInheritedScopes ( ctx , orgID , uid , folderSvc )
if err != nil {
return nil , err
}
return append ( inheritedScopes , ScopeFoldersProvider . GetResourceScopeUID ( uid ) ) , nil
} )
}
2022-06-07 04:02:20 -05:00
// NewDashboardIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "dashboards:id:"
// into uid based scopes for both dashboard and folder
2023-03-03 09:56:33 -06:00
func NewDashboardIDScopeResolver ( folderDB folder . FolderStore , ds DashboardService , folderSvc folder . Service ) ( string , ac . ScopeAttributeResolver ) {
2022-06-07 04:02:20 -05:00
prefix := ScopeDashboardsProvider . GetResourceScope ( "" )
return prefix , ac . ScopeAttributeResolverFunc ( func ( ctx context . Context , orgID int64 , scope string ) ( [ ] string , error ) {
if ! strings . HasPrefix ( scope , prefix ) {
return nil , ac . ErrInvalidScope
}
id , err := ac . ParseScopeID ( scope )
if err != nil {
return nil , err
}
2023-03-02 07:09:57 -06:00
dashboard , err := ds . GetDashboard ( ctx , & GetDashboardQuery { ID : id , OrgID : orgID } )
2022-06-07 04:02:20 -05:00
if err != nil {
return nil , err
}
2023-03-03 09:56:33 -06:00
return resolveDashboardScope ( ctx , folderDB , orgID , dashboard , folderSvc )
2022-06-07 04:02:20 -05:00
} )
}
// NewDashboardUIDScopeResolver provides an ScopeAttributeResolver that is able to convert a scope prefixed with "dashboards:uid:"
// into uid based scopes for both dashboard and folder
2023-03-03 09:56:33 -06:00
func NewDashboardUIDScopeResolver ( folderDB folder . FolderStore , ds DashboardService , folderSvc folder . Service ) ( string , ac . ScopeAttributeResolver ) {
2022-06-07 04:02:20 -05:00
prefix := ScopeDashboardsProvider . GetResourceScopeUID ( "" )
return prefix , ac . ScopeAttributeResolverFunc ( func ( ctx context . Context , orgID int64 , scope string ) ( [ ] string , error ) {
if ! strings . HasPrefix ( scope , prefix ) {
return nil , ac . ErrInvalidScope
}
uid , err := ac . ParseScopeUID ( scope )
if err != nil {
return nil , err
}
2023-03-02 07:09:57 -06:00
dashboard , err := ds . GetDashboard ( ctx , & GetDashboardQuery { UID : uid , OrgID : orgID } )
2022-06-07 04:02:20 -05:00
if err != nil {
return nil , err
}
2023-03-03 09:56:33 -06:00
return resolveDashboardScope ( ctx , folderDB , orgID , dashboard , folderSvc )
2022-06-07 04:02:20 -05:00
} )
}
2023-03-03 09:56:33 -06:00
func resolveDashboardScope ( ctx context . Context , folderDB folder . FolderStore , orgID int64 , dashboard * Dashboard , folderSvc folder . Service ) ( [ ] string , error ) {
2022-06-07 04:02:20 -05:00
var folderUID string
2023-01-16 09:33:55 -06:00
if dashboard . FolderID < 0 {
return [ ] string { ScopeDashboardsProvider . GetResourceScopeUID ( dashboard . UID ) } , nil
2022-08-09 03:14:08 -05:00
}
2023-01-16 09:33:55 -06:00
if dashboard . FolderID == 0 {
2022-06-07 04:02:20 -05:00
folderUID = ac . GeneralFolderUID
} else {
2023-03-03 09:56:33 -06:00
folder , err := folderDB . GetFolderByID ( ctx , orgID , dashboard . FolderID )
2022-06-07 04:02:20 -05:00
if err != nil {
return nil , err
}
2022-11-11 07:28:24 -06:00
folderUID = folder . UID
2022-06-07 04:02:20 -05:00
}
2022-08-09 03:14:08 -05:00
2023-01-30 08:19:42 -06:00
result , err := GetInheritedScopes ( ctx , orgID , folderUID , folderSvc )
2023-01-26 02:21:10 -06:00
if err != nil {
return nil , err
}
result = append ( [ ] string {
2023-01-16 09:33:55 -06:00
ScopeDashboardsProvider . GetResourceScopeUID ( dashboard . UID ) ,
2022-06-07 04:02:20 -05:00
ScopeFoldersProvider . GetResourceScopeUID ( folderUID ) ,
2023-01-26 02:21:10 -06:00
} ,
result ... ,
)
return result , nil
}
2023-01-30 08:19:42 -06:00
func GetInheritedScopes ( ctx context . Context , orgID int64 , folderUID string , folderSvc folder . Service ) ( [ ] string , error ) {
2023-05-04 03:36:36 -05:00
if folderUID == ac . GeneralFolderUID {
return nil , nil
}
2023-01-26 02:21:10 -06:00
ancestors , err := folderSvc . GetParents ( ctx , folder . GetParentsQuery {
UID : folderUID ,
OrgID : orgID ,
} )
if err != nil {
2023-01-30 08:19:42 -06:00
return nil , ac . ErrInternal . Errorf ( "could not retrieve folder parents: %w" , err )
2023-01-26 02:21:10 -06:00
}
result := make ( [ ] string , 0 , len ( ancestors ) )
for _ , ff := range ancestors {
result = append ( result , ScopeFoldersProvider . GetResourceScopeUID ( ff . UID ) )
}
return result , nil
2022-06-07 04:02:20 -05:00
}