mirror of
https://github.com/grafana/grafana.git
synced 2025-01-27 16:57:14 -06:00
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:
parent
f7c6491f73
commit
1f742fcf93
@ -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)
|
||||
}
|
||||
|
@ -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"`
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user