mirror of
https://github.com/grafana/grafana.git
synced 2024-11-29 20:24:18 -06:00
6b6cf5f4b7
move ViewersCanEdit into cfg
359 lines
12 KiB
Go
359 lines
12 KiB
Go
package guardian
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/grafana/grafana/pkg/infra/db"
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
"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/user"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
)
|
|
|
|
var permissionMap = map[string]dashboards.PermissionType{
|
|
"View": dashboards.PERMISSION_VIEW,
|
|
"Edit": dashboards.PERMISSION_EDIT,
|
|
"Admin": dashboards.PERMISSION_ADMIN,
|
|
}
|
|
|
|
var _ DashboardGuardian = new(AccessControlDashboardGuardian)
|
|
|
|
// NewAccessControlDashboardGuardianByDashboard creates a dashboard guardian by the provided dashboardId.
|
|
func NewAccessControlDashboardGuardian(
|
|
ctx context.Context, cfg *setting.Cfg, dashboardId int64, user *user.SignedInUser,
|
|
store db.DB, ac accesscontrol.AccessControl,
|
|
folderPermissionsService accesscontrol.FolderPermissionsService,
|
|
dashboardPermissionsService accesscontrol.DashboardPermissionsService,
|
|
dashboardService dashboards.DashboardService,
|
|
) (*AccessControlDashboardGuardian, error) {
|
|
var dashboard *dashboards.Dashboard
|
|
if dashboardId != 0 {
|
|
q := &dashboards.GetDashboardQuery{
|
|
ID: dashboardId,
|
|
OrgID: user.OrgID,
|
|
}
|
|
|
|
qResult, err := dashboardService.GetDashboard(ctx, q)
|
|
if err != nil {
|
|
if errors.Is(err, dashboards.ErrDashboardNotFound) {
|
|
return nil, ErrGuardianDashboardNotFound.Errorf("failed to get dashboard by UID: %w", err)
|
|
}
|
|
return nil, ErrGuardianGetDashboardFailure.Errorf("failed to get dashboard by UID: %w", err)
|
|
}
|
|
dashboard = qResult
|
|
}
|
|
|
|
return &AccessControlDashboardGuardian{
|
|
ctx: ctx,
|
|
cfg: cfg,
|
|
log: log.New("dashboard.permissions"),
|
|
dashboard: dashboard,
|
|
user: user,
|
|
store: store,
|
|
ac: ac,
|
|
folderPermissionsService: folderPermissionsService,
|
|
dashboardPermissionsService: dashboardPermissionsService,
|
|
dashboardService: dashboardService,
|
|
}, nil
|
|
}
|
|
|
|
// NewAccessControlDashboardGuardianByDashboard creates a dashboard guardian by the provided dashboardUID.
|
|
func NewAccessControlDashboardGuardianByUID(
|
|
ctx context.Context, cfg *setting.Cfg, dashboardUID string, user *user.SignedInUser,
|
|
store db.DB, ac accesscontrol.AccessControl,
|
|
folderPermissionsService accesscontrol.FolderPermissionsService,
|
|
dashboardPermissionsService accesscontrol.DashboardPermissionsService,
|
|
dashboardService dashboards.DashboardService,
|
|
) (*AccessControlDashboardGuardian, error) {
|
|
var dashboard *dashboards.Dashboard
|
|
if dashboardUID != "" {
|
|
q := &dashboards.GetDashboardQuery{
|
|
UID: dashboardUID,
|
|
OrgID: user.OrgID,
|
|
}
|
|
|
|
qResult, err := dashboardService.GetDashboard(ctx, q)
|
|
if err != nil {
|
|
if errors.Is(err, dashboards.ErrDashboardNotFound) {
|
|
return nil, ErrGuardianDashboardNotFound.Errorf("failed to get dashboard by UID: %w", err)
|
|
}
|
|
return nil, ErrGuardianGetDashboardFailure.Errorf("failed to get dashboard by UID: %w", err)
|
|
}
|
|
dashboard = qResult
|
|
}
|
|
|
|
return &AccessControlDashboardGuardian{
|
|
cfg: cfg,
|
|
ctx: ctx,
|
|
log: log.New("dashboard.permissions"),
|
|
dashboard: dashboard,
|
|
user: user,
|
|
store: store,
|
|
ac: ac,
|
|
folderPermissionsService: folderPermissionsService,
|
|
dashboardPermissionsService: dashboardPermissionsService,
|
|
dashboardService: dashboardService,
|
|
}, nil
|
|
}
|
|
|
|
// NewAccessControlDashboardGuardianByDashboard creates a dashboard guardian by the provided dashboard.
|
|
// This constructor should be preferred over the other two if the dashboard in available
|
|
// since it avoids querying the database for fetching the dashboard.
|
|
func NewAccessControlDashboardGuardianByDashboard(
|
|
ctx context.Context, cfg *setting.Cfg, dashboard *dashboards.Dashboard, user *user.SignedInUser,
|
|
store db.DB, ac accesscontrol.AccessControl,
|
|
folderPermissionsService accesscontrol.FolderPermissionsService,
|
|
dashboardPermissionsService accesscontrol.DashboardPermissionsService,
|
|
dashboardService dashboards.DashboardService,
|
|
) (*AccessControlDashboardGuardian, error) {
|
|
return &AccessControlDashboardGuardian{
|
|
cfg: cfg,
|
|
ctx: ctx,
|
|
log: log.New("dashboard.permissions"),
|
|
dashboard: dashboard,
|
|
user: user,
|
|
store: store,
|
|
ac: ac,
|
|
folderPermissionsService: folderPermissionsService,
|
|
dashboardPermissionsService: dashboardPermissionsService,
|
|
dashboardService: dashboardService,
|
|
}, nil
|
|
}
|
|
|
|
type AccessControlDashboardGuardian struct {
|
|
cfg *setting.Cfg
|
|
ctx context.Context
|
|
log log.Logger
|
|
dashboard *dashboards.Dashboard
|
|
user *user.SignedInUser
|
|
store db.DB
|
|
ac accesscontrol.AccessControl
|
|
folderPermissionsService accesscontrol.FolderPermissionsService
|
|
dashboardPermissionsService accesscontrol.DashboardPermissionsService
|
|
dashboardService dashboards.DashboardService
|
|
}
|
|
|
|
func (a *AccessControlDashboardGuardian) CanSave() (bool, error) {
|
|
if a.dashboard == nil {
|
|
return false, ErrGuardianDashboardNotFound
|
|
}
|
|
|
|
if a.dashboard.IsFolder {
|
|
return a.evaluate(accesscontrol.EvalPermission(dashboards.ActionFoldersWrite, dashboards.ScopeFoldersProvider.GetResourceScopeUID(a.dashboard.UID)))
|
|
}
|
|
|
|
return a.evaluate(
|
|
accesscontrol.EvalPermission(dashboards.ActionDashboardsWrite, dashboards.ScopeDashboardsProvider.GetResourceScopeUID(a.dashboard.UID)),
|
|
)
|
|
}
|
|
|
|
func (a *AccessControlDashboardGuardian) CanEdit() (bool, error) {
|
|
if a.dashboard == nil {
|
|
return false, ErrGuardianDashboardNotFound
|
|
}
|
|
|
|
if a.cfg.ViewersCanEdit {
|
|
return a.CanView()
|
|
}
|
|
|
|
if a.dashboard.IsFolder {
|
|
return a.evaluate(accesscontrol.EvalPermission(dashboards.ActionFoldersWrite, dashboards.ScopeFoldersProvider.GetResourceScopeUID(a.dashboard.UID)))
|
|
}
|
|
|
|
return a.evaluate(
|
|
accesscontrol.EvalPermission(dashboards.ActionDashboardsWrite, dashboards.ScopeDashboardsProvider.GetResourceScopeUID(a.dashboard.UID)),
|
|
)
|
|
}
|
|
|
|
func (a *AccessControlDashboardGuardian) CanView() (bool, error) {
|
|
if a.dashboard == nil {
|
|
return false, ErrGuardianDashboardNotFound
|
|
}
|
|
|
|
if a.dashboard.IsFolder {
|
|
return a.evaluate(accesscontrol.EvalPermission(dashboards.ActionFoldersRead, dashboards.ScopeFoldersProvider.GetResourceScopeUID(a.dashboard.UID)))
|
|
}
|
|
|
|
return a.evaluate(
|
|
accesscontrol.EvalPermission(dashboards.ActionDashboardsRead, dashboards.ScopeDashboardsProvider.GetResourceScopeUID(a.dashboard.UID)),
|
|
)
|
|
}
|
|
|
|
func (a *AccessControlDashboardGuardian) CanAdmin() (bool, error) {
|
|
if a.dashboard == nil {
|
|
return false, ErrGuardianDashboardNotFound
|
|
}
|
|
|
|
if a.dashboard.IsFolder {
|
|
return a.evaluate(accesscontrol.EvalAll(
|
|
accesscontrol.EvalPermission(dashboards.ActionFoldersPermissionsRead, dashboards.ScopeFoldersProvider.GetResourceScopeUID(a.dashboard.UID)),
|
|
accesscontrol.EvalPermission(dashboards.ActionFoldersPermissionsWrite, dashboards.ScopeFoldersProvider.GetResourceScopeUID(a.dashboard.UID)),
|
|
))
|
|
}
|
|
|
|
return a.evaluate(accesscontrol.EvalAll(
|
|
accesscontrol.EvalPermission(dashboards.ActionDashboardsPermissionsRead, dashboards.ScopeDashboardsProvider.GetResourceScopeUID(a.dashboard.UID)),
|
|
accesscontrol.EvalPermission(dashboards.ActionDashboardsPermissionsWrite, dashboards.ScopeDashboardsProvider.GetResourceScopeUID(a.dashboard.UID)),
|
|
))
|
|
}
|
|
|
|
func (a *AccessControlDashboardGuardian) CanDelete() (bool, error) {
|
|
if a.dashboard == nil {
|
|
return false, ErrGuardianDashboardNotFound
|
|
}
|
|
|
|
if a.dashboard.IsFolder {
|
|
return a.evaluate(accesscontrol.EvalPermission(dashboards.ActionFoldersDelete, dashboards.ScopeFoldersProvider.GetResourceScopeUID(a.dashboard.UID)))
|
|
}
|
|
|
|
return a.evaluate(
|
|
accesscontrol.EvalPermission(dashboards.ActionDashboardsDelete, dashboards.ScopeDashboardsProvider.GetResourceScopeUID(a.dashboard.UID)),
|
|
)
|
|
}
|
|
|
|
func (a *AccessControlDashboardGuardian) CanCreate(folderID int64, isFolder bool) (bool, error) {
|
|
if isFolder {
|
|
return a.evaluate(accesscontrol.EvalPermission(dashboards.ActionFoldersCreate))
|
|
}
|
|
folder, err := a.loadParentFolder(folderID)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return a.evaluate(accesscontrol.EvalPermission(dashboards.ActionDashboardsCreate, dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder.UID)))
|
|
}
|
|
|
|
func (a *AccessControlDashboardGuardian) evaluate(evaluator accesscontrol.Evaluator) (bool, error) {
|
|
ok, err := a.ac.Evaluate(a.ctx, a.user, evaluator)
|
|
if err != nil {
|
|
id := 0
|
|
if a.dashboard != nil {
|
|
id = int(a.dashboard.ID)
|
|
}
|
|
a.log.Debug("Failed to evaluate access control to folder or dashboard", "error", err, "userId", a.user.UserID, "id", id)
|
|
}
|
|
|
|
if !ok && err == nil {
|
|
id := 0
|
|
if a.dashboard != nil {
|
|
id = int(a.dashboard.ID)
|
|
}
|
|
a.log.Debug("Access denied to folder or dashboard", "userId", a.user.UserID, "id", id, "permissions", evaluator.GoString())
|
|
}
|
|
|
|
return ok, err
|
|
}
|
|
|
|
func (a *AccessControlDashboardGuardian) CheckPermissionBeforeUpdate(permission dashboards.PermissionType, updatePermissions []*dashboards.DashboardACL) (bool, error) {
|
|
// always true for access control
|
|
return true, nil
|
|
}
|
|
|
|
// GetACL translate access control permissions to dashboard acl info
|
|
func (a *AccessControlDashboardGuardian) GetACL() ([]*dashboards.DashboardACLInfoDTO, error) {
|
|
if a.dashboard == nil {
|
|
return nil, ErrGuardianGetDashboardFailure
|
|
}
|
|
|
|
var svc accesscontrol.PermissionsService
|
|
if a.dashboard.IsFolder {
|
|
svc = a.folderPermissionsService
|
|
} else {
|
|
svc = a.dashboardPermissionsService
|
|
}
|
|
|
|
permissions, err := svc.GetPermissions(a.ctx, a.user, a.dashboard.UID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
acl := make([]*dashboards.DashboardACLInfoDTO, 0, len(permissions))
|
|
for _, p := range permissions {
|
|
if !p.IsManaged {
|
|
continue
|
|
}
|
|
|
|
var role *org.RoleType
|
|
if p.BuiltInRole != "" {
|
|
tmp := org.RoleType(p.BuiltInRole)
|
|
role = &tmp
|
|
}
|
|
|
|
acl = append(acl, &dashboards.DashboardACLInfoDTO{
|
|
OrgID: a.dashboard.OrgID,
|
|
DashboardID: a.dashboard.ID,
|
|
FolderID: a.dashboard.FolderID,
|
|
Created: p.Created,
|
|
Updated: p.Updated,
|
|
UserID: p.UserId,
|
|
UserLogin: p.UserLogin,
|
|
UserEmail: p.UserEmail,
|
|
TeamID: p.TeamId,
|
|
TeamEmail: p.TeamEmail,
|
|
Team: p.Team,
|
|
Role: role,
|
|
Permission: permissionMap[svc.MapActions(p)],
|
|
PermissionName: permissionMap[svc.MapActions(p)].String(),
|
|
UID: a.dashboard.UID,
|
|
Title: a.dashboard.Title,
|
|
Slug: a.dashboard.Slug,
|
|
IsFolder: a.dashboard.IsFolder,
|
|
URL: a.dashboard.GetURL(),
|
|
Inherited: false,
|
|
})
|
|
}
|
|
|
|
return acl, nil
|
|
}
|
|
|
|
func (a *AccessControlDashboardGuardian) GetACLWithoutDuplicates() ([]*dashboards.DashboardACLInfoDTO, error) {
|
|
return a.GetACL()
|
|
}
|
|
|
|
func (a *AccessControlDashboardGuardian) GetHiddenACL(cfg *setting.Cfg) ([]*dashboards.DashboardACL, error) {
|
|
var hiddenACL []*dashboards.DashboardACL
|
|
if a.user.IsGrafanaAdmin {
|
|
return hiddenACL, nil
|
|
}
|
|
|
|
existingPermissions, err := a.GetACL()
|
|
if err != nil {
|
|
return hiddenACL, err
|
|
}
|
|
|
|
for _, item := range existingPermissions {
|
|
if item.Inherited || item.UserLogin == a.user.Login {
|
|
continue
|
|
}
|
|
|
|
if _, hidden := cfg.HiddenUsers[item.UserLogin]; hidden {
|
|
hiddenACL = append(hiddenACL, &dashboards.DashboardACL{
|
|
OrgID: item.OrgID,
|
|
DashboardID: item.DashboardID,
|
|
UserID: item.UserID,
|
|
TeamID: item.TeamID,
|
|
Role: item.Role,
|
|
Permission: item.Permission,
|
|
Created: item.Created,
|
|
Updated: item.Updated,
|
|
})
|
|
}
|
|
}
|
|
|
|
return hiddenACL, nil
|
|
}
|
|
|
|
func (a *AccessControlDashboardGuardian) loadParentFolder(folderID int64) (*dashboards.Dashboard, error) {
|
|
if folderID == 0 {
|
|
return &dashboards.Dashboard{UID: accesscontrol.GeneralFolderUID}, nil
|
|
}
|
|
folderQuery := &dashboards.GetDashboardQuery{ID: folderID, OrgID: a.user.OrgID}
|
|
folderQueryResult, err := a.dashboardService.GetDashboard(a.ctx, folderQuery)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return folderQueryResult, nil
|
|
}
|