Guardian: Split dashboard and folder guardian implementation (#69722)

* Split dashboard and folder guardian implementations

* Replace guardian constructors

* Simplify tests

* Add tests

* Apply suggestion from code review

Differentiate errors for dashboard and folders

* Remove tests for general folder

* Add tests for general scope
This commit is contained in:
Sofia Papagiannaki 2023-07-25 15:31:12 +03:00 committed by GitHub
parent f7c6491f73
commit 1f742fcf93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 985 additions and 229 deletions

View File

@ -86,7 +86,7 @@ func (hs *HTTPServer) GetFolderByUID(c *contextmodel.ReqContext) response.Respon
return apierrors.ToFolderErrorResponse(err)
}
g, err := guardian.NewByUID(c.Req.Context(), folder.UID, c.OrgID, c.SignedInUser)
g, err := guardian.NewByFolder(c.Req.Context(), folder, c.OrgID, c.SignedInUser)
if err != nil {
return response.Err(err)
}
@ -119,7 +119,7 @@ func (hs *HTTPServer) GetFolderByID(c *contextmodel.ReqContext) response.Respons
return apierrors.ToFolderErrorResponse(err)
}
g, err := guardian.NewByUID(c.Req.Context(), folder.UID, c.OrgID, c.SignedInUser)
g, err := guardian.NewByFolder(c.Req.Context(), folder, c.OrgID, c.SignedInUser)
if err != nil {
return response.Err(err)
}
@ -160,7 +160,7 @@ func (hs *HTTPServer) CreateFolder(c *contextmodel.ReqContext) response.Response
// Required for cases when caller wants to immediately interact with the newly created object
hs.accesscontrolService.ClearUserPermissionCache(c.SignedInUser)
g, err := guardian.NewByUID(c.Req.Context(), folder.UID, c.OrgID, c.SignedInUser)
g, err := guardian.NewByFolder(c.Req.Context(), folder, c.OrgID, c.SignedInUser)
if err != nil {
return response.Err(err)
}
@ -215,7 +215,7 @@ func (hs *HTTPServer) MoveFolder(c *contextmodel.ReqContext) response.Response {
return response.Error(http.StatusInternalServerError, "move folder failed", err)
}
g, err := guardian.NewByUID(c.Req.Context(), cmd.UID, c.OrgID, c.SignedInUser)
g, err := guardian.NewByFolder(c.Req.Context(), theFolder, c.OrgID, c.SignedInUser)
if err != nil {
return response.Err(err)
}
@ -251,7 +251,7 @@ func (hs *HTTPServer) UpdateFolder(c *contextmodel.ReqContext) response.Response
if err != nil {
return apierrors.ToFolderErrorResponse(err)
}
g, err := guardian.NewByUID(c.Req.Context(), result.UID, c.OrgID, c.SignedInUser)
g, err := guardian.NewByFolder(c.Req.Context(), result, c.OrgID, c.SignedInUser)
if err != nil {
return response.Err(err)
}

View File

@ -418,9 +418,10 @@ type DashboardACL struct {
func (p DashboardACL) TableName() string { return "dashboard_acl" }
type DashboardACLInfoDTO struct {
OrgID int64 `json:"-" xorm:"org_id"`
DashboardID int64 `json:"dashboardId,omitempty" xorm:"dashboard_id"`
FolderID int64 `json:"folderId,omitempty" xorm:"folder_id"`
OrgID int64 `json:"-" xorm:"org_id"`
DashboardID int64 `json:"dashboardId,omitempty" xorm:"dashboard_id"`
FolderID int64 `json:"folderId,omitempty" xorm:"folder_id"`
FolderUID string `json:"folderUid,omitempty" xorm:"folder_uid"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`

View File

@ -127,7 +127,7 @@ func (s *Service) Get(ctx context.Context, cmd *folder.GetFolderQuery) (*folder.
// do not get guardian by the folder ID because it differs from the nested folder ID
// and the legacy folder ID has been associated with the permissions:
// use the folde UID instead that is the same for both
g, err := guardian.NewByUID(ctx, dashFolder.UID, dashFolder.OrgID, cmd.SignedInUser)
g, err := guardian.NewByFolder(ctx, dashFolder, dashFolder.OrgID, cmd.SignedInUser)
if err != nil {
return nil, err
}
@ -204,7 +204,7 @@ func (s *Service) GetChildren(ctx context.Context, cmd *folder.GetChildrenQuery)
continue
}
g, err := guardian.NewByUID(ctx, f.UID, f.OrgID, cmd.SignedInUser)
g, err := guardian.NewByFolder(ctx, dashFolder, dashFolder.OrgID, cmd.SignedInUser)
if err != nil {
return nil, err
}

View File

@ -8,6 +8,7 @@ import (
"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/folder"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
@ -19,7 +20,7 @@ var permissionMap = map[string]dashboards.PermissionType{
"Admin": dashboards.PERMISSION_ADMIN,
}
var _ DashboardGuardian = new(AccessControlDashboardGuardian)
var _ DashboardGuardian = new(accessControlDashboardGuardian)
// NewAccessControlDashboardGuardianByDashboard creates a dashboard guardian by the provided dashboardId.
func NewAccessControlDashboardGuardian(
@ -28,7 +29,7 @@ func NewAccessControlDashboardGuardian(
folderPermissionsService accesscontrol.FolderPermissionsService,
dashboardPermissionsService accesscontrol.DashboardPermissionsService,
dashboardService dashboards.DashboardService,
) (*AccessControlDashboardGuardian, error) {
) (DashboardGuardian, error) {
var dashboard *dashboards.Dashboard
if dashboardId != 0 {
q := &dashboards.GetDashboardQuery{
@ -46,17 +47,34 @@ func NewAccessControlDashboardGuardian(
dashboard = qResult
}
return &AccessControlDashboardGuardian{
ctx: ctx,
cfg: cfg,
log: log.New("dashboard.permissions"),
if dashboard != nil && dashboard.IsFolder {
return &accessControlFolderGuardian{
accessControlBaseGuardian: accessControlBaseGuardian{
ctx: ctx,
cfg: cfg,
log: log.New("folder.permissions"),
user: user,
store: store,
ac: ac,
dashboardService: dashboardService,
},
folder: dashboards.FromDashboard(dashboard),
folderPermissionsService: folderPermissionsService,
}, nil
}
return &accessControlDashboardGuardian{
accessControlBaseGuardian: accessControlBaseGuardian{
ctx: ctx,
cfg: cfg,
log: log.New("dashboard.permissions"),
user: user,
store: store,
ac: ac,
dashboardService: dashboardService,
},
dashboard: dashboard,
user: user,
store: store,
ac: ac,
folderPermissionsService: folderPermissionsService,
dashboardPermissionsService: dashboardPermissionsService,
dashboardService: dashboardService,
}, nil
}
@ -67,7 +85,7 @@ func NewAccessControlDashboardGuardianByUID(
folderPermissionsService accesscontrol.FolderPermissionsService,
dashboardPermissionsService accesscontrol.DashboardPermissionsService,
dashboardService dashboards.DashboardService,
) (*AccessControlDashboardGuardian, error) {
) (DashboardGuardian, error) {
var dashboard *dashboards.Dashboard
if dashboardUID != "" {
q := &dashboards.GetDashboardQuery{
@ -85,17 +103,34 @@ func NewAccessControlDashboardGuardianByUID(
dashboard = qResult
}
return &AccessControlDashboardGuardian{
cfg: cfg,
ctx: ctx,
log: log.New("dashboard.permissions"),
if dashboard != nil && dashboard.IsFolder {
return &accessControlFolderGuardian{
accessControlBaseGuardian: accessControlBaseGuardian{
ctx: ctx,
cfg: cfg,
log: log.New("folder.permissions"),
user: user,
store: store,
ac: ac,
dashboardService: dashboardService,
},
folder: dashboards.FromDashboard(dashboard),
folderPermissionsService: folderPermissionsService,
}, nil
}
return &accessControlDashboardGuardian{
accessControlBaseGuardian: accessControlBaseGuardian{
cfg: cfg,
ctx: ctx,
log: log.New("dashboard.permissions"),
user: user,
store: store,
ac: ac,
dashboardService: dashboardService,
},
dashboard: dashboard,
user: user,
store: store,
ac: ac,
folderPermissionsService: folderPermissionsService,
dashboardPermissionsService: dashboardPermissionsService,
dashboardService: dashboardService,
}, nil
}
@ -108,41 +143,86 @@ func NewAccessControlDashboardGuardianByDashboard(
folderPermissionsService accesscontrol.FolderPermissionsService,
dashboardPermissionsService accesscontrol.DashboardPermissionsService,
dashboardService dashboards.DashboardService,
) (*AccessControlDashboardGuardian, error) {
return &AccessControlDashboardGuardian{
cfg: cfg,
ctx: ctx,
log: log.New("dashboard.permissions"),
) (DashboardGuardian, error) {
if dashboard != nil && dashboard.IsFolder {
return &accessControlFolderGuardian{
accessControlBaseGuardian: accessControlBaseGuardian{
ctx: ctx,
cfg: cfg,
log: log.New("folder.permissions"),
user: user,
store: store,
ac: ac,
dashboardService: dashboardService,
},
folder: dashboards.FromDashboard(dashboard),
folderPermissionsService: folderPermissionsService,
}, nil
}
return &accessControlDashboardGuardian{
accessControlBaseGuardian: accessControlBaseGuardian{
cfg: cfg,
ctx: ctx,
log: log.New("dashboard.permissions"),
user: user,
store: store,
ac: ac,
dashboardService: dashboardService,
},
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
// NewAccessControlFolderGuardian creates a folder guardian by the provided folder.
func NewAccessControlFolderGuardian(
ctx context.Context, cfg *setting.Cfg, f *folder.Folder, user *user.SignedInUser,
store db.DB, ac accesscontrol.AccessControl,
folderPermissionsService accesscontrol.FolderPermissionsService,
dashboardPermissionsService accesscontrol.DashboardPermissionsService,
dashboardService dashboards.DashboardService,
) (DashboardGuardian, error) {
return &accessControlFolderGuardian{
accessControlBaseGuardian: accessControlBaseGuardian{
ctx: ctx,
cfg: cfg,
log: log.New("folder.permissions"),
user: user,
store: store,
ac: ac,
dashboardService: dashboardService,
},
folder: f,
folderPermissionsService: folderPermissionsService,
}, nil
}
func (a *AccessControlDashboardGuardian) CanSave() (bool, error) {
if a.dashboard == nil {
return false, ErrGuardianDashboardNotFound
}
type accessControlBaseGuardian struct {
cfg *setting.Cfg
ctx context.Context
log log.Logger
user *user.SignedInUser
ac accesscontrol.AccessControl
store db.DB
dashboardService dashboards.DashboardService
}
if a.dashboard.IsFolder {
return a.evaluate(accesscontrol.EvalPermission(dashboards.ActionFoldersWrite, dashboards.ScopeFoldersProvider.GetResourceScopeUID(a.dashboard.UID)))
type accessControlDashboardGuardian struct {
accessControlBaseGuardian
dashboard *dashboards.Dashboard
dashboardPermissionsService accesscontrol.DashboardPermissionsService
}
type accessControlFolderGuardian struct {
accessControlBaseGuardian
folder *folder.Folder
folderPermissionsService accesscontrol.FolderPermissionsService
}
func (a *accessControlDashboardGuardian) CanSave() (bool, error) {
if a.dashboard == nil {
return false, ErrGuardianDashboardNotFound.Errorf("failed to check save permissions for dashboard")
}
return a.evaluate(
@ -150,31 +230,43 @@ func (a *AccessControlDashboardGuardian) CanSave() (bool, error) {
)
}
func (a *AccessControlDashboardGuardian) CanEdit() (bool, error) {
func (a *accessControlFolderGuardian) CanSave() (bool, error) {
if a.folder == nil {
return false, ErrGuardianFolderNotFound.Errorf("failed to check save permissions for folder")
}
return a.evaluate(accesscontrol.EvalPermission(dashboards.ActionFoldersWrite, dashboards.ScopeFoldersProvider.GetResourceScopeUID(a.folder.UID)))
}
func (a *accessControlDashboardGuardian) CanEdit() (bool, error) {
if a.dashboard == nil {
return false, ErrGuardianDashboardNotFound
return false, ErrGuardianDashboardNotFound.Errorf("failed to check edit permissions for dashboard")
}
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
func (a *accessControlFolderGuardian) CanEdit() (bool, error) {
if a.folder == nil {
return false, ErrGuardianFolderNotFound.Errorf("failed to check edit permissions for folder")
}
if a.dashboard.IsFolder {
return a.evaluate(accesscontrol.EvalPermission(dashboards.ActionFoldersRead, dashboards.ScopeFoldersProvider.GetResourceScopeUID(a.dashboard.UID)))
if a.cfg.ViewersCanEdit {
return a.CanView()
}
return a.evaluate(accesscontrol.EvalPermission(dashboards.ActionFoldersWrite, dashboards.ScopeFoldersProvider.GetResourceScopeUID(a.folder.UID)))
}
func (a *accessControlDashboardGuardian) CanView() (bool, error) {
if a.dashboard == nil {
return false, ErrGuardianDashboardNotFound.Errorf("failed to check view permissions for dashboard")
}
return a.evaluate(
@ -182,16 +274,17 @@ func (a *AccessControlDashboardGuardian) CanView() (bool, error) {
)
}
func (a *AccessControlDashboardGuardian) CanAdmin() (bool, error) {
if a.dashboard == nil {
return false, ErrGuardianDashboardNotFound
func (a *accessControlFolderGuardian) CanView() (bool, error) {
if a.folder == nil {
return false, ErrGuardianFolderNotFound.Errorf("failed to check view permissions for folder")
}
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.EvalPermission(dashboards.ActionFoldersRead, dashboards.ScopeFoldersProvider.GetResourceScopeUID(a.folder.UID)))
}
func (a *accessControlDashboardGuardian) CanAdmin() (bool, error) {
if a.dashboard == nil {
return false, ErrGuardianDashboardNotFound.Errorf("failed to check admin permissions for dashboard")
}
return a.evaluate(accesscontrol.EvalAll(
@ -200,13 +293,20 @@ func (a *AccessControlDashboardGuardian) CanAdmin() (bool, error) {
))
}
func (a *AccessControlDashboardGuardian) CanDelete() (bool, error) {
if a.dashboard == nil {
return false, ErrGuardianDashboardNotFound
func (a *accessControlFolderGuardian) CanAdmin() (bool, error) {
if a.folder == nil {
return false, ErrGuardianFolderNotFound.Errorf("failed to check admin permissions for folder")
}
if a.dashboard.IsFolder {
return a.evaluate(accesscontrol.EvalPermission(dashboards.ActionFoldersDelete, dashboards.ScopeFoldersProvider.GetResourceScopeUID(a.dashboard.UID)))
return a.evaluate(accesscontrol.EvalAll(
accesscontrol.EvalPermission(dashboards.ActionFoldersPermissionsRead, dashboards.ScopeFoldersProvider.GetResourceScopeUID(a.folder.UID)),
accesscontrol.EvalPermission(dashboards.ActionFoldersPermissionsWrite, dashboards.ScopeFoldersProvider.GetResourceScopeUID(a.folder.UID)),
))
}
func (a *accessControlDashboardGuardian) CanDelete() (bool, error) {
if a.dashboard == nil {
return false, ErrGuardianDashboardNotFound.Errorf("failed to check delete permissions for dashboard")
}
return a.evaluate(
@ -214,7 +314,15 @@ func (a *AccessControlDashboardGuardian) CanDelete() (bool, error) {
)
}
func (a *AccessControlDashboardGuardian) CanCreate(folderID int64, isFolder bool) (bool, error) {
func (a *accessControlFolderGuardian) CanDelete() (bool, error) {
if a.folder == nil {
return false, ErrGuardianFolderNotFound.Errorf("failed to check delete permissions for folder")
}
return a.evaluate(accesscontrol.EvalPermission(dashboards.ActionFoldersDelete, dashboards.ScopeFoldersProvider.GetResourceScopeUID(a.folder.UID)))
}
func (a *accessControlDashboardGuardian) CanCreate(folderID int64, isFolder bool) (bool, error) {
if isFolder {
return a.evaluate(accesscontrol.EvalPermission(dashboards.ActionFoldersCreate))
}
@ -225,14 +333,25 @@ func (a *AccessControlDashboardGuardian) CanCreate(folderID int64, isFolder bool
return a.evaluate(accesscontrol.EvalPermission(dashboards.ActionDashboardsCreate, dashboards.ScopeFoldersProvider.GetResourceScopeUID(folder.UID)))
}
func (a *AccessControlDashboardGuardian) evaluate(evaluator accesscontrol.Evaluator) (bool, error) {
func (a *accessControlFolderGuardian) 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)
a.log.Debug("Failed to evaluate access control to dashboard", "error", err, "userId", a.user.UserID, "id", id)
}
if !ok && err == nil {
@ -240,29 +359,49 @@ func (a *AccessControlDashboardGuardian) evaluate(evaluator accesscontrol.Evalua
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())
a.log.Debug("Access denied to 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) {
func (a *accessControlFolderGuardian) evaluate(evaluator accesscontrol.Evaluator) (bool, error) {
ok, err := a.ac.Evaluate(a.ctx, a.user, evaluator)
if err != nil {
uid := ""
orgID := 0
if a.folder != nil {
uid = a.folder.UID
orgID = int(a.folder.OrgID)
}
a.log.Debug("Failed to evaluate access control to folder", "error", err, "userId", a.user.UserID, "orgID", orgID, "uid", uid)
}
if !ok && err == nil {
uid := ""
orgID := 0
if a.folder != nil {
uid = a.folder.UID
orgID = int(a.folder.OrgID)
}
a.log.Debug("Access denied to folder", "userId", a.user.UserID, "orgID", orgID, "uid", uid, "permissions", evaluator.GoString())
}
return ok, err
}
func (a *accessControlBaseGuardian) 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) {
func (a *accessControlDashboardGuardian) GetACL() ([]*dashboards.DashboardACLInfoDTO, error) {
if a.dashboard == nil {
return nil, ErrGuardianGetDashboardFailure
return nil, ErrGuardianGetDashboardFailure.Errorf("failed to translate access control permissions to dashboard acl info")
}
var svc accesscontrol.PermissionsService
if a.dashboard.IsFolder {
svc = a.folderPermissionsService
} else {
svc = a.dashboardPermissionsService
}
svc := a.dashboardPermissionsService
permissions, err := svc.GetPermissions(a.ctx, a.user, a.dashboard.UID)
if err != nil {
@ -308,11 +447,67 @@ func (a *AccessControlDashboardGuardian) GetACL() ([]*dashboards.DashboardACLInf
return acl, nil
}
func (a *AccessControlDashboardGuardian) GetACLWithoutDuplicates() ([]*dashboards.DashboardACLInfoDTO, error) {
// GetACL translate access control permissions to dashboard acl info
func (a *accessControlFolderGuardian) GetACL() ([]*dashboards.DashboardACLInfoDTO, error) {
if a.folder == nil {
return nil, ErrGuardianGetFolderFailure.Errorf("failed to translate access control permissions to dashboard acl info")
}
svc := a.folderPermissionsService
permissions, err := svc.GetPermissions(a.ctx, a.user, a.folder.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.folder.OrgID,
DashboardID: a.folder.ID,
FolderUID: a.folder.ParentUID,
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.folder.UID,
Title: a.folder.Title,
//Slug: a.folder.Slug,
IsFolder: true,
URL: a.folder.WithURL().URL,
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) {
func (a *accessControlFolderGuardian) 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
@ -345,7 +540,52 @@ func (a *AccessControlDashboardGuardian) GetHiddenACL(cfg *setting.Cfg) ([]*dash
return hiddenACL, nil
}
func (a *AccessControlDashboardGuardian) loadParentFolder(folderID int64) (*dashboards.Dashboard, error) {
func (a *accessControlFolderGuardian) 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
}
func (a *accessControlFolderGuardian) loadParentFolder(folderID int64) (*dashboards.Dashboard, error) {
if folderID == 0 {
return &dashboards.Dashboard{UID: accesscontrol.GeneralFolderUID}, nil
}

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@ import (
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/team"
"github.com/grafana/grafana/pkg/services/user"
@ -18,7 +19,9 @@ var (
ErrGuardianPermissionExists = errors.New("permission already exists")
ErrGuardianOverride = errors.New("you can only override a permission to be higher")
ErrGuardianGetDashboardFailure = errutil.NewBase(errutil.StatusInternal, "guardian.getDashboardFailure", errutil.WithPublicMessage("Failed to get dashboard"))
ErrGuardianGetFolderFailure = errutil.NewBase(errutil.StatusInternal, "guardian.getFolderFailure", errutil.WithPublicMessage("Failed to get folder"))
ErrGuardianDashboardNotFound = errutil.NewBase(errutil.StatusNotFound, "guardian.dashboardNotFound")
ErrGuardianFolderNotFound = errutil.NewBase(errutil.StatusNotFound, "guardian.folderNotFound")
)
// DashboardGuardian to be used for guard against operations without access on dashboard and acl
@ -73,6 +76,12 @@ var NewByDashboard = func(ctx context.Context, dash *dashboards.Dashboard, orgId
panic("no guardian factory implementation provided")
}
// NewByFolder factory for creating a new folder guardian instance
// When using access control this function is replaced on startup and the AccessControlDashboardGuardian is returned
var NewByFolder = func(ctx context.Context, f *folder.Folder, orgId int64, user *user.SignedInUser) (DashboardGuardian, error) {
panic("no guardian factory implementation provided")
}
// newDashboardGuardian creates a dashboard guardian by the provided dashId.
func newDashboardGuardian(ctx context.Context, cfg *setting.Cfg, dashId int64, orgId int64, user *user.SignedInUser, store db.DB, dashSvc dashboards.DashboardService, teamSvc team.Service) (*dashboardGuardianImpl, error) {
if dashId != 0 {
@ -151,6 +160,24 @@ func newDashboardGuardianByDashboard(ctx context.Context, cfg *setting.Cfg, dash
}, nil
}
// newDashboardGuardianByFolder creates a dashboard guardian by the provided folder.
// This constructor should be preferred over the other two if the dashboard in available
// since it avoids querying the database for fetching the dashboard.
// The folder.ID should be the sequence ID in the dashboard table.
func newDashboardGuardianByFolder(ctx context.Context, cfg *setting.Cfg, f *folder.Folder, orgId int64, user *user.SignedInUser, store db.DB, dashSvc dashboards.DashboardService, teamSvc team.Service) (*dashboardGuardianImpl, error) {
return &dashboardGuardianImpl{
cfg: cfg,
user: user,
dashId: f.ID,
orgId: orgId,
log: log.New("dashboard.permissions"),
ctx: ctx,
store: store,
dashboardService: dashSvc,
teamService: teamSvc,
}, nil
}
func (g *dashboardGuardianImpl) CanSave() (bool, error) {
return g.HasPermission(dashboards.PERMISSION_EDIT)
}
@ -482,4 +509,12 @@ func MockDashboardGuardian(mock *FakeDashboardGuardian) {
mock.User = user
return mock, nil
}
NewByFolder = func(_ context.Context, f *folder.Folder, orgId int64, user *user.SignedInUser) (DashboardGuardian, error) {
mock.OrgID = orgId
mock.DashUID = f.UID
mock.DashID = f.ID
mock.User = user
return mock, nil
}
}

View File

@ -6,6 +6,7 @@ import (
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/team"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
@ -39,6 +40,10 @@ func InitLegacyGuardian(cfg *setting.Cfg, store db.DB, dashSvc dashboards.Dashbo
NewByDashboard = func(ctx context.Context, dash *dashboards.Dashboard, orgId int64, user *user.SignedInUser) (DashboardGuardian, error) {
return newDashboardGuardianByDashboard(ctx, cfg, dash, orgId, user, store, dashSvc, teamSvc)
}
NewByFolder = func(ctx context.Context, f *folder.Folder, orgId int64, user *user.SignedInUser) (DashboardGuardian, error) {
return newDashboardGuardianByFolder(ctx, cfg, f, orgId, user, store, dashSvc, teamSvc)
}
}
func InitAccessControlGuardian(
@ -56,4 +61,8 @@ func InitAccessControlGuardian(
NewByDashboard = func(ctx context.Context, dash *dashboards.Dashboard, orgId int64, user *user.SignedInUser) (DashboardGuardian, error) {
return NewAccessControlDashboardGuardianByDashboard(ctx, cfg, dash, user, store, ac, folderPermissionsService, dashboardPermissionsService, dashboardService)
}
NewByFolder = func(ctx context.Context, f *folder.Folder, orgId int64, user *user.SignedInUser) (DashboardGuardian, error) {
return NewAccessControlFolderGuardian(ctx, cfg, f, user, store, ac, folderPermissionsService, dashboardPermissionsService, dashboardService)
}
}