The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/services/guardian/accesscontrol_guardian.go

275 lines
8.9 KiB

package guardian
import (
"context"
"github.com/grafana/grafana/pkg/infra/log"
"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"
)
var permissionMap = map[string]models.PermissionType{
"View": models.PERMISSION_VIEW,
"Edit": models.PERMISSION_EDIT,
"Admin": models.PERMISSION_ADMIN,
}
var _ DashboardGuardian = new(AccessControlDashboardGuardian)
func NewAccessControlDashboardGuardian(
ctx context.Context, dashboardId int64, user *models.SignedInUser,
store sqlstore.Store, ac accesscontrol.AccessControl,
folderPermissionsService accesscontrol.FolderPermissionsService,
dashboardPermissionsService accesscontrol.DashboardPermissionsService,
dashboardService dashboards.DashboardService,
) *AccessControlDashboardGuardian {
return &AccessControlDashboardGuardian{
ctx: ctx,
log: log.New("dashboard.permissions"),
dashboardID: dashboardId,
user: user,
store: store,
ac: ac,
folderPermissionsService: folderPermissionsService,
dashboardPermissionsService: dashboardPermissionsService,
dashboardService: dashboardService,
}
}
type AccessControlDashboardGuardian struct {
ctx context.Context
log log.Logger
dashboardID int64
dashboard *models.Dashboard
user *models.SignedInUser
store sqlstore.Store
ac accesscontrol.AccessControl
folderPermissionsService accesscontrol.FolderPermissionsService
dashboardPermissionsService accesscontrol.DashboardPermissionsService
dashboardService dashboards.DashboardService
}
func (a *AccessControlDashboardGuardian) CanSave() (bool, error) {
if err := a.loadDashboard(); err != nil {
return false, err
}
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 err := a.loadDashboard(); err != nil {
return false, err
}
if setting.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 err := a.loadDashboard(); err != nil {
return false, err
}
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 err := a.loadDashboard(); err != nil {
return false, err
}
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 err := a.loadDashboard(); err != nil {
return false, err
}
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 {
a.log.Debug("Failed to evaluate access control to folder or dashboard", "error", err, "userId", a.user.UserId, "id", a.dashboardID)
}
if !ok && err == nil {
a.log.Debug("Access denied to folder or dashboard", "userId", a.user.UserId, "id", a.dashboardID, "permissions", evaluator.GoString())
}
return ok, err
}
func (a *AccessControlDashboardGuardian) CheckPermissionBeforeUpdate(permission models.PermissionType, updatePermissions []*models.DashboardACL) (bool, error) {
// always true for access control
return true, nil
}
// GetACL translate access control permissions to dashboard acl info
func (a *AccessControlDashboardGuardian) GetACL() ([]*models.DashboardACLInfoDTO, error) {
if err := a.loadDashboard(); err != nil {
return nil, err
}
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([]*models.DashboardACLInfoDTO, 0, len(permissions))
for _, p := range permissions {
if !p.IsManaged {
continue
}
var role *models.RoleType
if p.BuiltInRole != "" {
tmp := models.RoleType(p.BuiltInRole)
role = &tmp
}
acl = append(acl, &models.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() ([]*models.DashboardACLInfoDTO, error) {
return a.GetACL()
}
func (a *AccessControlDashboardGuardian) GetHiddenACL(cfg *setting.Cfg) ([]*models.DashboardACL, error) {
var hiddenACL []*models.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, &models.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) loadDashboard() error {
if a.dashboard == nil {
query := &models.GetDashboardQuery{Id: a.dashboardID, OrgId: a.user.OrgId}
if err := a.dashboardService.GetDashboard(a.ctx, query); err != nil {
return err
}
a.dashboard = query.Result
}
return nil
}
func (a *AccessControlDashboardGuardian) loadParentFolder(folderID int64) (*models.Dashboard, error) {
if folderID == 0 {
return &models.Dashboard{Uid: accesscontrol.GeneralFolderUID}, nil
}
folderQuery := &models.GetDashboardQuery{Id: folderID, OrgId: a.user.OrgId}
if err := a.dashboardService.GetDashboard(a.ctx, folderQuery); err != nil {
return nil, err
}
return folderQuery.Result, nil
}