mirror of
https://github.com/grafana/grafana.git
synced 2025-08-03 02:12:24 +08:00
226 lines
7.4 KiB
Go
226 lines
7.4 KiB
Go
package permissions
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"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/org"
|
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
|
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
)
|
|
|
|
type DashboardPermissionFilter struct {
|
|
OrgRole org.RoleType
|
|
Dialect migrator.Dialect
|
|
UserId int64
|
|
OrgId int64
|
|
PermissionLevel models.PermissionType
|
|
}
|
|
|
|
func (d DashboardPermissionFilter) Where() (string, []interface{}) {
|
|
if d.OrgRole == org.RoleAdmin {
|
|
return "", nil
|
|
}
|
|
|
|
okRoles := []interface{}{d.OrgRole}
|
|
if d.OrgRole == org.RoleEditor {
|
|
okRoles = append(okRoles, org.RoleViewer)
|
|
}
|
|
|
|
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
|
|
da.team_id IN (SELECT team_id from team_member AS tm WHERE tm.user_id = ?) OR
|
|
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
|
|
(folder.id IS NULL AND d.has_acl = ` + falseStr + `)
|
|
)
|
|
)
|
|
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
|
|
}
|
|
|
|
type AccessControlDashboardPermissionFilter struct {
|
|
user *user.SignedInUser
|
|
dashboardActions []string
|
|
folderActions []string
|
|
}
|
|
|
|
// NewAccessControlDashboardPermissionFilter creates a new AccessControlDashboardPermissionFilter that is configured with specific actions calculated based on the models.PermissionType and query type
|
|
func NewAccessControlDashboardPermissionFilter(user *user.SignedInUser, permissionLevel models.PermissionType, queryType string) AccessControlDashboardPermissionFilter {
|
|
needEdit := permissionLevel > models.PERMISSION_VIEW
|
|
|
|
var folderActions []string
|
|
var dashboardActions []string
|
|
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,
|
|
)
|
|
if needEdit {
|
|
folderActions = append(
|
|
folderActions,
|
|
accesscontrol.ActionAlertingRuleCreate,
|
|
)
|
|
}
|
|
} else {
|
|
folderActions = append(folderActions, dashboards.ActionFoldersRead)
|
|
dashboardActions = append(dashboardActions, dashboards.ActionDashboardsRead)
|
|
if needEdit {
|
|
folderActions = append(folderActions, dashboards.ActionDashboardsCreate)
|
|
dashboardActions = append(dashboardActions, dashboards.ActionDashboardsWrite)
|
|
}
|
|
}
|
|
|
|
return AccessControlDashboardPermissionFilter{user: user, folderActions: folderActions, dashboardActions: dashboardActions}
|
|
}
|
|
|
|
func (f AccessControlDashboardPermissionFilter) Where() (string, []interface{}) {
|
|
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))
|
|
rolesFilter := " AND role_id IN(SELECT id FROM role " + filter + ") "
|
|
var args []interface{}
|
|
builder := strings.Builder{}
|
|
builder.WriteRune('(')
|
|
if len(f.dashboardActions) > 0 {
|
|
toCheck := actionsToCheck(f.dashboardActions, f.user.Permissions[f.user.OrgID], dashWildcards, folderWildcards)
|
|
|
|
if len(toCheck) > 0 {
|
|
builder.WriteString("(dashboard.uid IN (SELECT substr(scope, 16) FROM permission WHERE scope LIKE 'dashboards:uid:%'")
|
|
builder.WriteString(rolesFilter)
|
|
args = append(args, params...)
|
|
|
|
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)")
|
|
|
|
builder.WriteString(" OR ")
|
|
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)
|
|
args = append(args, params...)
|
|
|
|
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)")
|
|
} else {
|
|
builder.WriteString("NOT dashboard.is_folder")
|
|
}
|
|
}
|
|
|
|
if len(f.folderActions) > 0 {
|
|
if len(f.dashboardActions) > 0 {
|
|
builder.WriteString(" OR ")
|
|
}
|
|
|
|
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)
|
|
args = append(args, params...)
|
|
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)")
|
|
} else {
|
|
builder.WriteString("dashboard.is_folder")
|
|
}
|
|
}
|
|
builder.WriteRune(')')
|
|
return builder.String(), args
|
|
}
|
|
|
|
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
|
|
|
|
outer:
|
|
for _, scope := range permissions[a] {
|
|
for _, w := range wildcards {
|
|
if w.Contains(scope) {
|
|
hasWildcard = true
|
|
break outer
|
|
}
|
|
}
|
|
}
|
|
|
|
if !hasWildcard {
|
|
toCheck = append(toCheck, a)
|
|
}
|
|
}
|
|
return toCheck
|
|
}
|