2020-04-20 16:20:45 +02:00
package permissions
import (
2020-06-29 14:08:32 +02:00
"strings"
2020-04-20 16:20:45 +02:00
"github.com/grafana/grafana/pkg/models"
2022-03-03 15:05:47 +01:00
"github.com/grafana/grafana/pkg/services/accesscontrol"
2022-03-09 11:57:50 -05:00
"github.com/grafana/grafana/pkg/services/dashboards"
2022-08-10 11:56:48 +02:00
"github.com/grafana/grafana/pkg/services/org"
2020-04-20 16:20:45 +02:00
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
2022-03-16 10:07:04 -04:00
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
2022-08-10 11:56:48 +02:00
"github.com/grafana/grafana/pkg/services/user"
2020-04-20 16:20:45 +02:00
)
type DashboardPermissionFilter struct {
2022-08-10 11:56:48 +02:00
OrgRole org . RoleType
2020-04-20 16:20:45 +02:00
Dialect migrator . Dialect
UserId int64
OrgId int64
PermissionLevel models . PermissionType
}
func ( d DashboardPermissionFilter ) Where ( ) ( string , [ ] interface { } ) {
2022-08-10 11:56:48 +02:00
if d . OrgRole == org . RoleAdmin {
2020-04-20 16:20:45 +02:00
return "" , nil
}
okRoles := [ ] interface { } { d . OrgRole }
2022-08-10 11:56:48 +02:00
if d . OrgRole == org . RoleEditor {
okRoles = append ( okRoles , org . RoleViewer )
2020-04-20 16:20:45 +02:00
}
falseStr := d . Dialect . BooleanStr ( false )
sql := ` (
dashboard . id IN (
SELECT distinct DashboardId from (
SELECT d . id AS DashboardId
FROM dashboard AS d
LEFT JOIN dashboard_acl AS da ON
da . dashboard_id = d . id OR
da . dashboard_id = d . folder_id
WHERE
d . org_id = ? AND
da . permission >= ? AND
(
da . user_id = ? OR
2022-01-04 13:04:02 +01:00
da . team_id IN ( SELECT team_id from team_member AS tm WHERE tm . user_id = ? ) OR
2020-04-20 16:20:45 +02:00
da . role IN ( ? ` + strings.Repeat(",?", len(okRoles)-1) + ` )
)
UNION
SELECT d . id AS DashboardId
FROM dashboard AS d
LEFT JOIN dashboard AS folder on folder . id = d . folder_id
LEFT JOIN dashboard_acl AS da ON
(
-- include default permissions -- >
da . org_id = - 1 AND (
( folder . id IS NOT NULL AND folder . has_acl = ` + falseStr + ` ) OR
2022-03-03 15:05:47 +01:00
( folder . id IS NULL AND d . has_acl = ` + falseStr + ` )
2020-04-20 16:20:45 +02:00
)
)
WHERE
d . org_id = ? AND
da . permission >= ? AND
(
da . user_id = ? OR
da . role IN ( ? ` + strings.Repeat(",?", len(okRoles)-1) + ` )
)
) AS a
)
)
`
params := [ ] interface { } { d . OrgId , d . PermissionLevel , d . UserId , d . UserId }
params = append ( params , okRoles ... )
params = append ( params , d . OrgId , d . PermissionLevel , d . UserId )
params = append ( params , okRoles ... )
return sql , params
}
2022-03-03 15:05:47 +01:00
type AccessControlDashboardPermissionFilter struct {
2022-10-25 11:14:27 +02:00
user * user . SignedInUser
dashboardActions [ ] string
2023-01-09 14:38:57 +01:00
folderActions [ ] string
2022-03-03 15:05:47 +01:00
}
2022-03-16 10:07:04 -04:00
// NewAccessControlDashboardPermissionFilter creates a new AccessControlDashboardPermissionFilter that is configured with specific actions calculated based on the models.PermissionType and query type
2022-08-10 11:56:48 +02:00
func NewAccessControlDashboardPermissionFilter ( user * user . SignedInUser , permissionLevel models . PermissionType , queryType string ) AccessControlDashboardPermissionFilter {
2022-03-16 10:07:04 -04:00
needEdit := permissionLevel > models . PERMISSION_VIEW
2023-01-09 14:38:57 +01:00
var folderActions [ ] string
2022-03-16 10:07:04 -04:00
var dashboardActions [ ] string
2023-01-09 14:38:57 +01:00
if queryType == searchstore . TypeFolder {
folderActions = append ( folderActions , dashboards . ActionFoldersRead )
if needEdit {
folderActions = append ( folderActions , dashboards . ActionDashboardsCreate )
}
} else if queryType == searchstore . TypeDashboard {
dashboardActions = append ( dashboardActions , dashboards . ActionDashboardsRead )
if needEdit {
dashboardActions = append ( dashboardActions , dashboards . ActionDashboardsWrite )
}
} else if queryType == searchstore . TypeAlertFolder {
folderActions = append (
folderActions ,
dashboards . ActionFoldersRead ,
accesscontrol . ActionAlertingRuleRead ,
)
2022-03-16 10:07:04 -04:00
if needEdit {
2023-01-09 14:38:57 +01:00
folderActions = append (
folderActions ,
accesscontrol . ActionAlertingRuleCreate ,
)
2022-03-16 10:07:04 -04:00
}
} else {
2023-01-09 14:38:57 +01:00
folderActions = append ( folderActions , dashboards . ActionFoldersRead )
2022-05-04 16:12:09 +02:00
dashboardActions = append ( dashboardActions , dashboards . ActionDashboardsRead )
2022-03-16 10:07:04 -04:00
if needEdit {
2022-05-04 16:12:09 +02:00
folderActions = append ( folderActions , dashboards . ActionDashboardsCreate )
dashboardActions = append ( dashboardActions , dashboards . ActionDashboardsWrite )
2022-03-16 10:07:04 -04:00
}
2022-03-08 12:46:49 +01:00
}
2023-01-09 14:38:57 +01:00
2022-10-25 11:14:27 +02:00
return AccessControlDashboardPermissionFilter { user : user , folderActions : folderActions , dashboardActions : dashboardActions }
2022-03-16 10:07:04 -04:00
}
2022-03-08 12:46:49 +01:00
2022-03-16 10:07:04 -04:00
func ( f AccessControlDashboardPermissionFilter ) Where ( ) ( string , [ ] interface { } ) {
2022-10-25 11:14:27 +02:00
if f . user == nil || f . user . Permissions == nil || f . user . Permissions [ f . user . OrgID ] == nil {
return "(1 = 0)" , nil
}
dashWildcards := accesscontrol . WildcardsFromPrefix ( dashboards . ScopeDashboardsPrefix )
folderWildcards := accesscontrol . WildcardsFromPrefix ( dashboards . ScopeFoldersPrefix )
filter , params := accesscontrol . UserRolesFilter ( f . user . OrgID , f . user . UserID , f . user . Teams , accesscontrol . GetOrgRoles ( f . user ) )
2023-01-09 14:38:57 +01:00
rolesFilter := " AND role_id IN(SELECT id FROM role " + filter + ") "
2022-03-14 17:11:21 +01:00
var args [ ] interface { }
2022-03-03 15:05:47 +01:00
builder := strings . Builder { }
2022-10-25 11:14:27 +02:00
builder . WriteRune ( '(' )
2022-03-16 10:07:04 -04:00
if len ( f . dashboardActions ) > 0 {
2023-01-09 14:38:57 +01:00
toCheck := actionsToCheck ( f . dashboardActions , f . user . Permissions [ f . user . OrgID ] , dashWildcards , folderWildcards )
2022-03-03 15:05:47 +01:00
2023-01-09 14:38:57 +01:00
if len ( toCheck ) > 0 {
builder . WriteString ( "(dashboard.uid IN (SELECT substr(scope, 16) FROM permission WHERE scope LIKE 'dashboards:uid:%'" )
builder . WriteString ( rolesFilter )
2022-10-25 11:14:27 +02:00
args = append ( args , params ... )
2023-01-09 14:38:57 +01:00
if len ( toCheck ) == 1 {
builder . WriteString ( " AND action = ?" )
args = append ( args , toCheck [ 0 ] )
} else {
builder . WriteString ( " AND action IN (?" + strings . Repeat ( ", ?" , len ( toCheck ) - 1 ) + ") GROUP BY role_id, scope HAVING COUNT(action) = ?" )
args = append ( args , toCheck ... )
args = append ( args , len ( toCheck ) )
}
builder . WriteString ( ") AND NOT dashboard.is_folder)" )
2022-03-03 15:05:47 +01:00
2022-10-25 11:14:27 +02:00
builder . WriteString ( " OR " )
2023-01-09 14:38:57 +01:00
builder . WriteString ( "(dashboard.folder_id IN (SELECT id FROM dashboard as d WHERE d.uid IN (SELECT substr(scope, 13) FROM permission WHERE scope LIKE 'folders:uid:%' " )
builder . WriteString ( rolesFilter )
2022-10-25 11:14:27 +02:00
args = append ( args , params ... )
2023-01-09 14:38:57 +01:00
if len ( toCheck ) == 1 {
builder . WriteString ( " AND action = ?" )
args = append ( args , toCheck [ 0 ] )
} else {
builder . WriteString ( " AND action IN (?" + strings . Repeat ( ", ?" , len ( toCheck ) - 1 ) + ") GROUP BY role_id, scope HAVING COUNT(action) = ?" )
args = append ( args , toCheck ... )
args = append ( args , len ( toCheck ) )
}
builder . WriteString ( ")) AND NOT dashboard.is_folder)" )
2022-10-25 11:14:27 +02:00
} else {
builder . WriteString ( "NOT dashboard.is_folder" )
}
2022-03-16 10:07:04 -04:00
}
2022-03-03 15:05:47 +01:00
2022-03-16 10:07:04 -04:00
if len ( f . folderActions ) > 0 {
if len ( f . dashboardActions ) > 0 {
builder . WriteString ( " OR " )
}
2022-10-25 11:14:27 +02:00
2023-01-09 14:38:57 +01:00
toCheck := actionsToCheck ( f . folderActions , f . user . Permissions [ f . user . OrgID ] , folderWildcards )
if len ( toCheck ) > 0 {
builder . WriteString ( "(dashboard.uid IN (SELECT substr(scope, 13) FROM permission WHERE scope LIKE 'folders:uid:%'" )
builder . WriteString ( rolesFilter )
2022-10-25 11:14:27 +02:00
args = append ( args , params ... )
2023-01-09 14:38:57 +01:00
if len ( toCheck ) == 1 {
builder . WriteString ( " AND action = ?" )
args = append ( args , toCheck [ 0 ] )
} else {
builder . WriteString ( " AND action IN (?" + strings . Repeat ( ", ?" , len ( toCheck ) - 1 ) + ") GROUP BY role_id, scope HAVING COUNT(action) = ?" )
args = append ( args , toCheck ... )
args = append ( args , len ( toCheck ) )
}
builder . WriteString ( ") AND dashboard.is_folder)" )
2022-10-25 11:14:27 +02:00
} else {
builder . WriteString ( "dashboard.is_folder" )
}
2022-03-16 10:07:04 -04:00
}
2022-10-25 11:14:27 +02:00
builder . WriteRune ( ')' )
2022-03-14 17:11:21 +01:00
return builder . String ( ) , args
2022-03-03 15:05:47 +01:00
}
2023-01-09 14:38:57 +01:00
func actionsToCheck ( actions [ ] string , permissions map [ string ] [ ] string , wildcards ... accesscontrol . Wildcards ) [ ] interface { } {
toCheck := make ( [ ] interface { } , 0 , len ( actions ) )
for _ , a := range actions {
var hasWildcard bool
for _ , scope := range permissions [ a ] {
for _ , w := range wildcards {
if w . Contains ( scope ) {
hasWildcard = true
break
}
}
}
if ! hasWildcard {
toCheck = append ( toCheck , a )
}
}
return toCheck
}