mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
authz: Clean up acl endpoints and dashboard guardian (#73746)
* RBAC: remove unnessisary guardian construction and update tests * RBAC: remove usage of guardian in UpdateFolderPermissions and refactor test * RBAC: remove usage of guardian in update and get permissions for dashboards
This commit is contained in:
parent
19ae937aa8
commit
05c386504b
@ -11,13 +11,11 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/api/routing"
|
"github.com/grafana/grafana/pkg/api/routing"
|
||||||
"github.com/grafana/grafana/pkg/infra/db"
|
"github.com/grafana/grafana/pkg/infra/db"
|
||||||
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
|
||||||
"github.com/grafana/grafana/pkg/infra/fs"
|
"github.com/grafana/grafana/pkg/infra/fs"
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||||
"github.com/grafana/grafana/pkg/models/usertoken"
|
"github.com/grafana/grafana/pkg/models/usertoken"
|
||||||
@ -29,10 +27,8 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/authn/authntest"
|
"github.com/grafana/grafana/pkg/services/authn/authntest"
|
||||||
"github.com/grafana/grafana/pkg/services/contexthandler"
|
"github.com/grafana/grafana/pkg/services/contexthandler"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
||||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
"github.com/grafana/grafana/pkg/services/guardian"
|
|
||||||
"github.com/grafana/grafana/pkg/services/licensing"
|
"github.com/grafana/grafana/pkg/services/licensing"
|
||||||
"github.com/grafana/grafana/pkg/services/login"
|
"github.com/grafana/grafana/pkg/services/login"
|
||||||
"github.com/grafana/grafana/pkg/services/login/logintest"
|
"github.com/grafana/grafana/pkg/services/login/logintest"
|
||||||
@ -41,7 +37,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/search"
|
"github.com/grafana/grafana/pkg/services/search"
|
||||||
"github.com/grafana/grafana/pkg/services/search/model"
|
"github.com/grafana/grafana/pkg/services/search/model"
|
||||||
"github.com/grafana/grafana/pkg/services/searchusers"
|
"github.com/grafana/grafana/pkg/services/searchusers"
|
||||||
"github.com/grafana/grafana/pkg/services/team/teamtest"
|
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
"github.com/grafana/grafana/pkg/services/user"
|
||||||
"github.com/grafana/grafana/pkg/services/user/usertest"
|
"github.com/grafana/grafana/pkg/services/user/usertest"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
@ -296,31 +291,10 @@ var (
|
|||||||
editorRole = org.RoleEditor
|
editorRole = org.RoleEditor
|
||||||
)
|
)
|
||||||
|
|
||||||
type setUpConf struct {
|
|
||||||
aclMockResp []*dashboards.DashboardACLInfoDTO
|
|
||||||
}
|
|
||||||
|
|
||||||
type mockSearchService struct{ ExpectedResult model.HitList }
|
type mockSearchService struct{ ExpectedResult model.HitList }
|
||||||
|
|
||||||
func (mss *mockSearchService) SearchHandler(_ context.Context, q *search.Query) (model.HitList, error) {
|
func (mss *mockSearchService) SearchHandler(_ context.Context, q *search.Query) (model.HitList, error) {
|
||||||
return mss.ExpectedResult, nil
|
return mss.ExpectedResult, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mss *mockSearchService) SortOptions() []model.SortOption { return nil }
|
func (mss *mockSearchService) SortOptions() []model.SortOption { return nil }
|
||||||
|
|
||||||
func setUp(confs ...setUpConf) *HTTPServer {
|
|
||||||
store := dbtest.NewFakeDB()
|
|
||||||
hs := &HTTPServer{SQLStore: store, SearchService: &mockSearchService{}}
|
|
||||||
|
|
||||||
aclMockResp := []*dashboards.DashboardACLInfoDTO{}
|
|
||||||
for _, c := range confs {
|
|
||||||
if c.aclMockResp != nil {
|
|
||||||
aclMockResp = c.aclMockResp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
teamSvc := &teamtest.FakeService{}
|
|
||||||
dashSvc := &dashboards.FakeDashboardService{}
|
|
||||||
qResult := aclMockResp
|
|
||||||
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardACLInfoListQuery")).Return(qResult, nil)
|
|
||||||
guardian.InitLegacyGuardian(setting.NewCfg(), store, dashSvc, teamSvc)
|
|
||||||
return hs
|
|
||||||
}
|
|
||||||
|
@ -2,7 +2,6 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
@ -10,9 +9,10 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/guardian"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -57,16 +57,7 @@ func (hs *HTTPServer) GetDashboardPermissionList(c *contextmodel.ReqContext) res
|
|||||||
return rsp
|
return rsp
|
||||||
}
|
}
|
||||||
|
|
||||||
g, err := guardian.NewByDashboard(c.Req.Context(), dash, c.OrgID, c.SignedInUser)
|
acl, err := hs.getDashboardACL(c.Req.Context(), c.SignedInUser, dash)
|
||||||
if err != nil {
|
|
||||||
return response.Err(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if canAdmin, err := g.CanAdmin(); err != nil || !canAdmin {
|
|
||||||
return dashboardGuardianResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
acl, err := g.GetACLWithoutDuplicates()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(500, "Failed to get dashboard permissions", err)
|
return response.Error(500, "Failed to get dashboard permissions", err)
|
||||||
}
|
}
|
||||||
@ -147,15 +138,6 @@ func (hs *HTTPServer) UpdateDashboardPermissions(c *contextmodel.ReqContext) res
|
|||||||
return rsp
|
return rsp
|
||||||
}
|
}
|
||||||
|
|
||||||
g, err := guardian.NewByDashboard(c.Req.Context(), dash, c.OrgID, c.SignedInUser)
|
|
||||||
if err != nil {
|
|
||||||
return response.Err(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if canAdmin, err := g.CanAdmin(); err != nil || !canAdmin {
|
|
||||||
return dashboardGuardianResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
items := make([]*dashboards.DashboardACL, 0, len(apiCmd.Items))
|
items := make([]*dashboards.DashboardACL, 0, len(apiCmd.Items))
|
||||||
for _, item := range apiCmd.Items {
|
for _, item := range apiCmd.Items {
|
||||||
items = append(items, &dashboards.DashboardACL{
|
items = append(items, &dashboards.DashboardACL{
|
||||||
@ -170,34 +152,102 @@ func (hs *HTTPServer) UpdateDashboardPermissions(c *contextmodel.ReqContext) res
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
hiddenACL, err := g.GetHiddenACL(hs.Cfg)
|
acl, err := hs.getDashboardACL(c.Req.Context(), c.SignedInUser, dash)
|
||||||
if err != nil {
|
|
||||||
return response.Error(http.StatusInternalServerError, "Error while retrieving hidden permissions", err)
|
|
||||||
}
|
|
||||||
items = append(items, hiddenACL...)
|
|
||||||
|
|
||||||
if okToUpdate, err := g.CheckPermissionBeforeUpdate(dashboards.PERMISSION_ADMIN, items); err != nil || !okToUpdate {
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, guardian.ErrGuardianPermissionExists) || errors.Is(err, guardian.ErrGuardianOverride) {
|
|
||||||
return response.Error(http.StatusBadRequest, err.Error(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.Error(http.StatusInternalServerError, "Error while checking dashboard permissions", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.Error(http.StatusForbidden, "Cannot remove own admin permission for a folder", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
old, err := g.GetACL()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(http.StatusInternalServerError, "Error while checking dashboard permissions", err)
|
return response.Error(http.StatusInternalServerError, "Error while checking dashboard permissions", err)
|
||||||
}
|
}
|
||||||
if err := hs.updateDashboardAccessControl(c.Req.Context(), dash.OrgID, dash.UID, false, items, old); err != nil {
|
|
||||||
|
items = append(items, hs.filterHiddenACL(c.SignedInUser, acl)...)
|
||||||
|
|
||||||
|
if err := hs.updateDashboardAccessControl(c.Req.Context(), dash.OrgID, dash.UID, false, items, acl); err != nil {
|
||||||
return response.Error(http.StatusInternalServerError, "Failed to update permissions", err)
|
return response.Error(http.StatusInternalServerError, "Failed to update permissions", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Success("Dashboard permissions updated")
|
return response.Success("Dashboard permissions updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var dashboardPermissionMap = map[string]dashboards.PermissionType{
|
||||||
|
"View": dashboards.PERMISSION_VIEW,
|
||||||
|
"Edit": dashboards.PERMISSION_EDIT,
|
||||||
|
"Admin": dashboards.PERMISSION_ADMIN,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hs *HTTPServer) getDashboardACL(ctx context.Context, user identity.Requester, dashboard *dashboards.Dashboard) ([]*dashboards.DashboardACLInfoDTO, error) {
|
||||||
|
permissions, err := hs.dashboardPermissionsService.GetPermissions(ctx, user, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
permission := dashboardPermissionMap[hs.dashboardPermissionsService.MapActions(p)]
|
||||||
|
|
||||||
|
acl = append(acl, &dashboards.DashboardACLInfoDTO{
|
||||||
|
OrgID: dashboard.OrgID,
|
||||||
|
DashboardID: dashboard.ID,
|
||||||
|
FolderID: 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: permission,
|
||||||
|
PermissionName: permission.String(),
|
||||||
|
UID: dashboard.UID,
|
||||||
|
Title: dashboard.Title,
|
||||||
|
Slug: dashboard.Slug,
|
||||||
|
IsFolder: dashboard.IsFolder,
|
||||||
|
URL: dashboard.GetURL(),
|
||||||
|
Inherited: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return acl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hs *HTTPServer) filterHiddenACL(user identity.Requester, acl []*dashboards.DashboardACLInfoDTO) []*dashboards.DashboardACL {
|
||||||
|
var hiddenACL []*dashboards.DashboardACL
|
||||||
|
|
||||||
|
if user.GetIsGrafanaAdmin() {
|
||||||
|
return hiddenACL
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range acl {
|
||||||
|
if item.Inherited || item.UserLogin == user.GetLogin() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, hidden := hs.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
|
||||||
|
}
|
||||||
|
|
||||||
// updateDashboardAccessControl is used for api backward compatibility
|
// updateDashboardAccessControl is used for api backward compatibility
|
||||||
func (hs *HTTPServer) updateDashboardAccessControl(ctx context.Context, orgID int64, uid string, isFolder bool, items []*dashboards.DashboardACL, old []*dashboards.DashboardACLInfoDTO) error {
|
func (hs *HTTPServer) updateDashboardAccessControl(ctx context.Context, orgID int64, uid string, isFolder bool, items []*dashboards.DashboardACL, old []*dashboards.DashboardACLInfoDTO) error {
|
||||||
commands := []accesscontrol.SetResourcePermissionCommand{}
|
commands := []accesscontrol.SetResourcePermissionCommand{}
|
||||||
|
@ -2,370 +2,158 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
|
||||||
"github.com/grafana/grafana/pkg/api/routing"
|
|
||||||
"github.com/grafana/grafana/pkg/bus"
|
|
||||||
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
|
||||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
dashboardservice "github.com/grafana/grafana/pkg/services/dashboards/service"
|
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
|
||||||
"github.com/grafana/grafana/pkg/services/folder/folderimpl"
|
|
||||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
|
||||||
"github.com/grafana/grafana/pkg/services/guardian"
|
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
"github.com/grafana/grafana/pkg/web/webtest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
func TestHTTPServer_GetDashboardPermissionList(t *testing.T) {
|
||||||
t.Run("Dashboard permissions test", func(t *testing.T) {
|
t.Run("should not be able to list acl when user does not have permission to do so", func(t *testing.T) {
|
||||||
settings := setting.NewCfg()
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {})
|
||||||
dashboardStore := &dashboards.FakeDashboardStore{}
|
|
||||||
qResult := &dashboards.Dashboard{}
|
|
||||||
dashboardStore.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(qResult, nil)
|
|
||||||
defer dashboardStore.AssertExpectations(t)
|
|
||||||
|
|
||||||
features := featuremgmt.WithFeatures()
|
res, err := server.Send(webtest.RequestWithSignedInUser(server.NewGetRequest("/api/dashboards/uid/1/permissions"), userWithPermissions(1, nil)))
|
||||||
mockSQLStore := dbtest.NewFakeDB()
|
|
||||||
ac := accesscontrolmock.New()
|
|
||||||
folderPermissions := accesscontrolmock.NewMockedPermissionsService()
|
|
||||||
dashboardPermissions := accesscontrolmock.NewMockedPermissionsService()
|
|
||||||
|
|
||||||
folderSvc := folderimpl.ProvideService(ac, bus.ProvideBus(tracing.InitializeTracerForTest()), settings, dashboardStore, foldertest.NewFakeFolderStore(t), mockSQLStore, featuremgmt.WithFeatures())
|
|
||||||
dashboardService, err := dashboardservice.ProvideDashboardServiceImpl(
|
|
||||||
settings, dashboardStore, foldertest.NewFakeFolderStore(t), nil, features, folderPermissions, dashboardPermissions, ac,
|
|
||||||
folderSvc,
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
hs := &HTTPServer{
|
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
||||||
Cfg: settings,
|
require.NoError(t, res.Body.Close())
|
||||||
SQLStore: mockSQLStore,
|
})
|
||||||
Features: features,
|
|
||||||
DashboardService: dashboardService,
|
|
||||||
AccessControl: accesscontrolmock.New(),
|
|
||||||
dashboardPermissionsService: dashboardPermissions,
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Given user has no admin permissions", func(t *testing.T) {
|
t.Run("should be able to list acl with correct permission", func(t *testing.T) {
|
||||||
origNewGuardian := guardian.New
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
t.Cleanup(func() {
|
svc := dashboards.NewFakeDashboardService(t)
|
||||||
guardian.New = origNewGuardian
|
svc.On("GetDashboard", mock.Anything, mock.Anything).Return(&dashboards.Dashboard{ID: 1, UID: "1"}, nil)
|
||||||
})
|
hs.DashboardService = svc
|
||||||
|
hs.dashboardPermissionsService = &actest.FakePermissionsService{
|
||||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanAdminValue: false})
|
ExpectedPermissions: []accesscontrol.ResourcePermission{},
|
||||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/1/permissions",
|
|
||||||
"/api/dashboards/id/:dashboardId/permissions", org.RoleEditor, func(sc *scenarioContext) {
|
|
||||||
callGetDashboardPermissions(sc, hs)
|
|
||||||
assert.Equal(t, 403, sc.resp.Code)
|
|
||||||
}, mockSQLStore)
|
|
||||||
|
|
||||||
cmd := dtos.UpdateDashboardACLCommand{
|
|
||||||
Items: []dtos.DashboardACLUpdateItem{
|
|
||||||
{UserID: 1000, Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDashboardPermissionScenario(t, updatePermissionContext{
|
|
||||||
desc: "When calling POST on",
|
|
||||||
url: "/api/dashboards/id/1/permissions",
|
|
||||||
routePattern: "/api/dashboards/id/:dashboardId/permissions",
|
|
||||||
cmd: cmd,
|
|
||||||
fn: func(sc *scenarioContext) {
|
|
||||||
callUpdateDashboardPermissions(t, sc)
|
|
||||||
assert.Equal(t, 403, sc.resp.Code)
|
|
||||||
},
|
|
||||||
}, hs)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Given user has admin permissions and permissions to update", func(t *testing.T) {
|
|
||||||
origNewGuardian := guardian.New
|
|
||||||
t.Cleanup(func() {
|
|
||||||
guardian.New = origNewGuardian
|
|
||||||
})
|
|
||||||
|
|
||||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
|
|
||||||
CanAdminValue: true,
|
|
||||||
CheckPermissionBeforeUpdateValue: true,
|
|
||||||
GetACLValue: []*dashboards.DashboardACLInfoDTO{
|
|
||||||
{OrgID: 1, DashboardID: 1, UserID: 2, Permission: dashboards.PERMISSION_VIEW},
|
|
||||||
{OrgID: 1, DashboardID: 1, UserID: 3, Permission: dashboards.PERMISSION_EDIT},
|
|
||||||
{OrgID: 1, DashboardID: 1, UserID: 4, Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
{OrgID: 1, DashboardID: 1, TeamID: 1, Permission: dashboards.PERMISSION_VIEW},
|
|
||||||
{OrgID: 1, DashboardID: 1, TeamID: 2, Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/1/permissions",
|
|
||||||
"/api/dashboards/id/:dashboardId/permissions", org.RoleAdmin, func(sc *scenarioContext) {
|
|
||||||
callGetDashboardPermissions(sc, hs)
|
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
|
||||||
|
|
||||||
var resp []*dashboards.DashboardACLInfoDTO
|
|
||||||
err := json.Unmarshal(sc.resp.Body.Bytes(), &resp)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Len(t, resp, 5)
|
|
||||||
assert.Equal(t, int64(2), resp[0].UserID)
|
|
||||||
assert.Equal(t, dashboards.PERMISSION_VIEW, resp[0].Permission)
|
|
||||||
}, mockSQLStore)
|
|
||||||
|
|
||||||
cmd := dtos.UpdateDashboardACLCommand{
|
|
||||||
Items: []dtos.DashboardACLUpdateItem{
|
|
||||||
{UserID: 1000, Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
dashboardPermissions.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil).Once()
|
|
||||||
updateDashboardPermissionScenario(t, updatePermissionContext{
|
|
||||||
desc: "When calling POST on",
|
|
||||||
url: "/api/dashboards/id/1/permissions",
|
|
||||||
routePattern: "/api/dashboards/id/:dashboardId/permissions",
|
|
||||||
cmd: cmd,
|
|
||||||
fn: func(sc *scenarioContext) {
|
|
||||||
callUpdateDashboardPermissions(t, sc)
|
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
|
||||||
},
|
|
||||||
}, hs)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("When trying to add permissions with both a team and user", func(t *testing.T) {
|
|
||||||
origNewGuardian := guardian.New
|
|
||||||
t.Cleanup(func() {
|
|
||||||
guardian.New = origNewGuardian
|
|
||||||
})
|
|
||||||
|
|
||||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
|
|
||||||
CanAdminValue: true,
|
|
||||||
CheckPermissionBeforeUpdateValue: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
cmd := dtos.UpdateDashboardACLCommand{
|
|
||||||
Items: []dtos.DashboardACLUpdateItem{
|
|
||||||
{UserID: 1000, TeamID: 1, Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDashboardPermissionScenario(t, updatePermissionContext{
|
|
||||||
desc: "When calling POST on",
|
|
||||||
url: "/api/dashboards/id/1/permissions",
|
|
||||||
routePattern: "/api/dashboards/id/:dashboardId/permissions",
|
|
||||||
cmd: cmd,
|
|
||||||
fn: func(sc *scenarioContext) {
|
|
||||||
callUpdateDashboardPermissions(t, sc)
|
|
||||||
assert.Equal(t, 400, sc.resp.Code)
|
|
||||||
respJSON, err := jsonMap(sc.resp.Body.Bytes())
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, dashboards.ErrPermissionsWithUserAndTeamNotAllowed.Error(), respJSON["error"])
|
|
||||||
},
|
|
||||||
}, hs)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("When trying to update permissions with duplicate permissions", func(t *testing.T) {
|
|
||||||
origNewGuardian := guardian.New
|
|
||||||
t.Cleanup(func() {
|
|
||||||
guardian.New = origNewGuardian
|
|
||||||
})
|
|
||||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
|
|
||||||
CanAdminValue: true,
|
|
||||||
CheckPermissionBeforeUpdateValue: false,
|
|
||||||
CheckPermissionBeforeUpdateError: guardian.ErrGuardianPermissionExists,
|
|
||||||
})
|
|
||||||
|
|
||||||
cmd := dtos.UpdateDashboardACLCommand{
|
|
||||||
Items: []dtos.DashboardACLUpdateItem{
|
|
||||||
{UserID: 1000, Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDashboardPermissionScenario(t, updatePermissionContext{
|
|
||||||
desc: "When calling POST on",
|
|
||||||
url: "/api/dashboards/id/1/permissions",
|
|
||||||
routePattern: "/api/dashboards/id/:dashboardId/permissions",
|
|
||||||
cmd: cmd,
|
|
||||||
fn: func(sc *scenarioContext) {
|
|
||||||
callUpdateDashboardPermissions(t, sc)
|
|
||||||
assert.Equal(t, 400, sc.resp.Code)
|
|
||||||
},
|
|
||||||
}, hs)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("When trying to update team or user permissions with a role", func(t *testing.T) {
|
|
||||||
role := org.RoleEditor
|
|
||||||
cmds := []dtos.UpdateDashboardACLCommand{
|
|
||||||
{
|
|
||||||
Items: []dtos.DashboardACLUpdateItem{
|
|
||||||
{UserID: 1000, Permission: dashboards.PERMISSION_ADMIN, Role: &role},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Items: []dtos.DashboardACLUpdateItem{
|
|
||||||
{TeamID: 1000, Permission: dashboards.PERMISSION_ADMIN, Role: &role},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, cmd := range cmds {
|
|
||||||
updateDashboardPermissionScenario(t, updatePermissionContext{
|
|
||||||
desc: "When calling POST on",
|
|
||||||
url: "/api/dashboards/id/1/permissions",
|
|
||||||
routePattern: "/api/dashboards/id/:dashboardId/permissions",
|
|
||||||
cmd: cmd,
|
|
||||||
fn: func(sc *scenarioContext) {
|
|
||||||
callUpdateDashboardPermissions(t, sc)
|
|
||||||
assert.Equal(t, 400, sc.resp.Code)
|
|
||||||
respJSON, err := jsonMap(sc.resp.Body.Bytes())
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, dashboards.ErrPermissionsWithRoleNotAllowed.Error(), respJSON["error"])
|
|
||||||
},
|
|
||||||
}, hs)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("When trying to override inherited permissions with lower precedence", func(t *testing.T) {
|
res, err := server.Send(webtest.RequestWithSignedInUser(server.NewGetRequest("/api/dashboards/uid/1/permissions"), userWithPermissions(1, []accesscontrol.Permission{
|
||||||
origNewGuardian := guardian.New
|
{Action: dashboards.ActionDashboardsPermissionsRead, Scope: "dashboards:uid:1"},
|
||||||
t.Cleanup(func() {
|
})))
|
||||||
guardian.New = origNewGuardian
|
|
||||||
})
|
|
||||||
|
|
||||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
|
require.NoError(t, err)
|
||||||
CanAdminValue: true,
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
CheckPermissionBeforeUpdateValue: false,
|
require.NoError(t, res.Body.Close())
|
||||||
CheckPermissionBeforeUpdateError: guardian.ErrGuardianOverride},
|
})
|
||||||
)
|
|
||||||
|
|
||||||
cmd := dtos.UpdateDashboardACLCommand{
|
t.Run("should filter out hidden users from acl", func(t *testing.T) {
|
||||||
Items: []dtos.DashboardACLUpdateItem{
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
{UserID: 1000, Permission: dashboards.PERMISSION_ADMIN},
|
cfg := setting.NewCfg()
|
||||||
|
cfg.HiddenUsers = map[string]struct{}{"hidden": {}}
|
||||||
|
hs.Cfg = cfg
|
||||||
|
|
||||||
|
svc := dashboards.NewFakeDashboardService(t)
|
||||||
|
svc.On("GetDashboard", mock.Anything, mock.Anything).Return(&dashboards.Dashboard{ID: 1, UID: "1"}, nil)
|
||||||
|
|
||||||
|
hs.DashboardService = svc
|
||||||
|
hs.dashboardPermissionsService = &actest.FakePermissionsService{
|
||||||
|
ExpectedPermissions: []accesscontrol.ResourcePermission{
|
||||||
|
{UserId: 1, UserLogin: "regular", IsManaged: true},
|
||||||
|
{UserId: 2, UserLogin: "hidden", IsManaged: true},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDashboardPermissionScenario(t, updatePermissionContext{
|
|
||||||
desc: "When calling POST on",
|
|
||||||
url: "/api/dashboards/id/1/permissions",
|
|
||||||
routePattern: "/api/dashboards/id/:dashboardId/permissions",
|
|
||||||
cmd: cmd,
|
|
||||||
fn: func(sc *scenarioContext) {
|
|
||||||
callUpdateDashboardPermissions(t, sc)
|
|
||||||
assert.Equal(t, 400, sc.resp.Code)
|
|
||||||
},
|
|
||||||
}, hs)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Getting and updating dashboard permissions with hidden users", func(t *testing.T) {
|
res, err := server.Send(webtest.RequestWithSignedInUser(server.NewGetRequest("/api/dashboards/uid/1/permissions"), userWithPermissions(1, []accesscontrol.Permission{
|
||||||
origNewGuardian := guardian.New
|
{Action: dashboards.ActionDashboardsPermissionsRead, Scope: "dashboards:uid:1"},
|
||||||
settings.HiddenUsers = map[string]struct{}{
|
})))
|
||||||
"hiddenUser": {},
|
|
||||||
testUserLogin: {},
|
|
||||||
}
|
|
||||||
t.Cleanup(func() {
|
|
||||||
guardian.New = origNewGuardian
|
|
||||||
settings.HiddenUsers = make(map[string]struct{})
|
|
||||||
})
|
|
||||||
|
|
||||||
mockSQLStore := dbtest.NewFakeDB()
|
require.NoError(t, err)
|
||||||
var resp []*dashboards.DashboardACLInfoDTO
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/1/permissions",
|
|
||||||
"/api/dashboards/id/:dashboardId/permissions", org.RoleAdmin, func(sc *scenarioContext) {
|
|
||||||
setUp()
|
|
||||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
|
|
||||||
CanAdminValue: true,
|
|
||||||
CheckPermissionBeforeUpdateValue: true,
|
|
||||||
GetACLValue: []*dashboards.DashboardACLInfoDTO{
|
|
||||||
{OrgID: 1, DashboardID: 1, UserID: 2, UserLogin: "hiddenUser", Permission: dashboards.PERMISSION_VIEW},
|
|
||||||
{OrgID: 1, DashboardID: 1, UserID: 3, UserLogin: testUserLogin, Permission: dashboards.PERMISSION_EDIT},
|
|
||||||
{OrgID: 1, DashboardID: 1, UserID: 4, UserLogin: "user_1", Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
},
|
|
||||||
GetHiddenACLValue: []*dashboards.DashboardACL{
|
|
||||||
{OrgID: 1, DashboardID: 1, UserID: 2, Permission: dashboards.PERMISSION_VIEW},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
callGetDashboardPermissions(sc, hs)
|
var result []dashboards.DashboardACLInfoDTO
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
require.NoError(t, json.NewDecoder(res.Body).Decode(&result))
|
||||||
|
|
||||||
err := json.Unmarshal(sc.resp.Body.Bytes(), &resp)
|
assert.Len(t, result, 1)
|
||||||
require.NoError(t, err)
|
assert.Equal(t, result[0].UserLogin, "regular")
|
||||||
|
require.NoError(t, res.Body.Close())
|
||||||
assert.Len(t, resp, 2)
|
|
||||||
assert.Equal(t, int64(3), resp[0].UserID)
|
|
||||||
assert.Equal(t, dashboards.PERMISSION_EDIT, resp[0].Permission)
|
|
||||||
assert.Equal(t, int64(4), resp[1].UserID)
|
|
||||||
assert.Equal(t, dashboards.PERMISSION_ADMIN, resp[1].Permission)
|
|
||||||
}, mockSQLStore)
|
|
||||||
|
|
||||||
cmd := dtos.UpdateDashboardACLCommand{
|
|
||||||
Items: []dtos.DashboardACLUpdateItem{
|
|
||||||
{UserID: 1000, Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, acl := range resp {
|
|
||||||
cmd.Items = append(cmd.Items, dtos.DashboardACLUpdateItem{
|
|
||||||
UserID: acl.UserID,
|
|
||||||
Permission: acl.Permission,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
assert.Len(t, cmd.Items, 3)
|
|
||||||
|
|
||||||
dashboardPermissions.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil).Once()
|
|
||||||
updateDashboardPermissionScenario(t, updatePermissionContext{
|
|
||||||
desc: "When calling POST on",
|
|
||||||
url: "/api/dashboards/id/1/permissions",
|
|
||||||
routePattern: "/api/dashboards/id/:dashboardId/permissions",
|
|
||||||
cmd: cmd,
|
|
||||||
fn: func(sc *scenarioContext) {
|
|
||||||
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
|
||||||
},
|
|
||||||
}, hs)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func callGetDashboardPermissions(sc *scenarioContext, hs *HTTPServer) {
|
func TestHTTPServer_UpdateDashboardPermissions(t *testing.T) {
|
||||||
sc.handlerFunc = hs.GetDashboardPermissionList
|
t.Run("should not be able to update acl when user does not have permission to do so", func(t *testing.T) {
|
||||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {})
|
||||||
}
|
|
||||||
|
|
||||||
func callUpdateDashboardPermissions(t *testing.T, sc *scenarioContext) {
|
res, err := server.Send(webtest.RequestWithSignedInUser(server.NewPostRequest("/api/dashboards/uid/1/permissions", nil), userWithPermissions(1, nil)))
|
||||||
t.Helper()
|
require.NoError(t, err)
|
||||||
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
||||||
}
|
require.NoError(t, res.Body.Close())
|
||||||
|
})
|
||||||
|
|
||||||
type updatePermissionContext struct {
|
t.Run("should be able to update acl with correct permissions", func(t *testing.T) {
|
||||||
desc string
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
url string
|
svc := dashboards.NewFakeDashboardService(t)
|
||||||
routePattern string
|
svc.On("GetDashboard", mock.Anything, mock.Anything).Return(&dashboards.Dashboard{ID: 1, UID: "1"}, nil)
|
||||||
cmd dtos.UpdateDashboardACLCommand
|
|
||||||
fn scenarioFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateDashboardPermissionScenario(t *testing.T, ctx updatePermissionContext, hs *HTTPServer) {
|
hs.DashboardService = svc
|
||||||
t.Run(fmt.Sprintf("%s %s", ctx.desc, ctx.url), func(t *testing.T) {
|
hs.dashboardPermissionsService = &actest.FakePermissionsService{}
|
||||||
sc := setupScenarioContext(t, ctx.url)
|
|
||||||
|
|
||||||
sc.defaultHandler = routing.Wrap(func(c *contextmodel.ReqContext) response.Response {
|
|
||||||
c.Req.Body = mockRequestBody(ctx.cmd)
|
|
||||||
c.Req.Header.Add("Content-Type", "application/json")
|
|
||||||
sc.context = c
|
|
||||||
sc.context.OrgID = testOrgID
|
|
||||||
sc.context.UserID = testUserID
|
|
||||||
|
|
||||||
return hs.UpdateDashboardPermissions(c)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
sc.m.Post(ctx.routePattern, sc.defaultHandler)
|
body := `{"items": []}`
|
||||||
|
res, err := server.SendJSON(webtest.RequestWithSignedInUser(server.NewPostRequest("/api/dashboards/uid/1/permissions", strings.NewReader(body)), userWithPermissions(1, []accesscontrol.Permission{
|
||||||
|
{Action: dashboards.ActionDashboardsPermissionsWrite, Scope: "dashboards:uid:1"},
|
||||||
|
})))
|
||||||
|
|
||||||
ctx.fn(sc)
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
|
require.NoError(t, res.Body.Close())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should not be able to specify team and user in same acl", func(t *testing.T) {
|
||||||
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
|
hs.DashboardService = dashboards.NewFakeDashboardService(t)
|
||||||
|
hs.dashboardPermissionsService = &actest.FakePermissionsService{}
|
||||||
|
})
|
||||||
|
|
||||||
|
body := `{"items": [{ userId:1, teamId: 2 }]}`
|
||||||
|
res, err := server.SendJSON(webtest.RequestWithSignedInUser(server.NewPostRequest("/api/dashboards/uid/1/permissions", strings.NewReader(body)), userWithPermissions(1, []accesscontrol.Permission{
|
||||||
|
{Action: dashboards.ActionDashboardsPermissionsWrite, Scope: "dashboards:uid:1"},
|
||||||
|
})))
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, http.StatusBadRequest, res.StatusCode)
|
||||||
|
require.NoError(t, res.Body.Close())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should not be able to specify team and role in same acl", func(t *testing.T) {
|
||||||
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
|
hs.DashboardService = dashboards.NewFakeDashboardService(t)
|
||||||
|
hs.dashboardPermissionsService = &actest.FakePermissionsService{}
|
||||||
|
})
|
||||||
|
|
||||||
|
body := `{"items": [{ teamId:1, role: "Admin" }]}`
|
||||||
|
res, err := server.SendJSON(webtest.RequestWithSignedInUser(server.NewPostRequest("/api/dashboards/uid/1/permissions", strings.NewReader(body)), userWithPermissions(1, []accesscontrol.Permission{
|
||||||
|
{Action: dashboards.ActionDashboardsPermissionsWrite, Scope: "dashboards:uid:1"},
|
||||||
|
})))
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, http.StatusBadRequest, res.StatusCode)
|
||||||
|
require.NoError(t, res.Body.Close())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should not be able to specify user and role in same acl", func(t *testing.T) {
|
||||||
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
|
hs.DashboardService = dashboards.NewFakeDashboardService(t)
|
||||||
|
hs.dashboardPermissionsService = &actest.FakePermissionsService{}
|
||||||
|
})
|
||||||
|
|
||||||
|
body := `{"items": [{ userId:1, role: "Admin" }]}`
|
||||||
|
res, err := server.SendJSON(webtest.RequestWithSignedInUser(server.NewPostRequest("/api/dashboards/uid/1/permissions", strings.NewReader(body)), userWithPermissions(1, []accesscontrol.Permission{
|
||||||
|
{Action: dashboards.ActionDashboardsPermissionsWrite, Scope: "dashboards:uid:1"},
|
||||||
|
})))
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, http.StatusBadRequest, res.StatusCode)
|
||||||
|
require.NoError(t, res.Body.Close())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,18 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/apierrors"
|
"github.com/grafana/grafana/pkg/api/apierrors"
|
||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
"github.com/grafana/grafana/pkg/api/dtos"
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
"github.com/grafana/grafana/pkg/api/response"
|
||||||
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
"github.com/grafana/grafana/pkg/services/guardian"
|
"github.com/grafana/grafana/pkg/services/org"
|
||||||
"github.com/grafana/grafana/pkg/web"
|
"github.com/grafana/grafana/pkg/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -33,16 +34,7 @@ func (hs *HTTPServer) GetFolderPermissionList(c *contextmodel.ReqContext) respon
|
|||||||
return apierrors.ToFolderErrorResponse(err)
|
return apierrors.ToFolderErrorResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
g, err := guardian.New(c.Req.Context(), folder.ID, c.OrgID, c.SignedInUser)
|
acl, err := hs.getFolderACL(c.Req.Context(), c.SignedInUser, folder)
|
||||||
if err != nil {
|
|
||||||
return response.Err(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if canAdmin, err := g.CanAdmin(); err != nil || !canAdmin {
|
|
||||||
return apierrors.ToFolderErrorResponse(dashboards.ErrFolderAccessDenied)
|
|
||||||
}
|
|
||||||
|
|
||||||
acl, err := g.GetACL()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(500, "Failed to get folder permissions", err)
|
return response.Error(500, "Failed to get folder permissions", err)
|
||||||
}
|
}
|
||||||
@ -97,20 +89,6 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *contextmodel.ReqContext) respon
|
|||||||
return apierrors.ToFolderErrorResponse(err)
|
return apierrors.ToFolderErrorResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
g, err := guardian.New(c.Req.Context(), folder.ID, c.OrgID, c.SignedInUser)
|
|
||||||
if err != nil {
|
|
||||||
return response.Err(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
canAdmin, err := g.CanAdmin()
|
|
||||||
if err != nil {
|
|
||||||
return apierrors.ToFolderErrorResponse(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !canAdmin {
|
|
||||||
return apierrors.ToFolderErrorResponse(dashboards.ErrFolderAccessDenied)
|
|
||||||
}
|
|
||||||
|
|
||||||
items := make([]*dashboards.DashboardACL, 0, len(apiCmd.Items))
|
items := make([]*dashboards.DashboardACL, 0, len(apiCmd.Items))
|
||||||
for _, item := range apiCmd.Items {
|
for _, item := range apiCmd.Items {
|
||||||
items = append(items, &dashboards.DashboardACL{
|
items = append(items, &dashboards.DashboardACL{
|
||||||
@ -125,35 +103,72 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *contextmodel.ReqContext) respon
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
hiddenACL, err := g.GetHiddenACL(hs.Cfg)
|
acl, err := hs.getFolderACL(c.Req.Context(), c.SignedInUser, folder)
|
||||||
if err != nil {
|
|
||||||
return response.Error(http.StatusInternalServerError, "Error while retrieving hidden permissions", err)
|
|
||||||
}
|
|
||||||
items = append(items, hiddenACL...)
|
|
||||||
|
|
||||||
if okToUpdate, err := g.CheckPermissionBeforeUpdate(dashboards.PERMISSION_ADMIN, items); err != nil || !okToUpdate {
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, guardian.ErrGuardianPermissionExists) ||
|
|
||||||
errors.Is(err, guardian.ErrGuardianOverride) {
|
|
||||||
return response.Error(http.StatusBadRequest, err.Error(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.Error(http.StatusInternalServerError, "Error while checking folder permissions", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.Error(http.StatusForbidden, "Cannot remove own admin permission for a folder", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
old, err := g.GetACL()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.Error(http.StatusInternalServerError, "Error while checking folder permissions", err)
|
return response.Error(http.StatusInternalServerError, "Error while checking folder permissions", err)
|
||||||
}
|
}
|
||||||
if err := hs.updateDashboardAccessControl(c.Req.Context(), c.OrgID, folder.UID, true, items, old); err != nil {
|
|
||||||
|
items = append(items, hs.filterHiddenACL(c.SignedInUser, acl)...)
|
||||||
|
|
||||||
|
if err := hs.updateDashboardAccessControl(c.Req.Context(), c.OrgID, folder.UID, true, items, acl); err != nil {
|
||||||
return response.Error(http.StatusInternalServerError, "Failed to create permission", err)
|
return response.Error(http.StatusInternalServerError, "Failed to create permission", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Success("Folder permissions updated")
|
return response.Success("Folder permissions updated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var folderPermissionMap = map[string]dashboards.PermissionType{
|
||||||
|
"View": dashboards.PERMISSION_VIEW,
|
||||||
|
"Edit": dashboards.PERMISSION_EDIT,
|
||||||
|
"Admin": dashboards.PERMISSION_ADMIN,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hs *HTTPServer) getFolderACL(ctx context.Context, user identity.Requester, folder *folder.Folder) ([]*dashboards.DashboardACLInfoDTO, error) {
|
||||||
|
permissions, err := hs.folderPermissionsService.GetPermissions(ctx, user, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
permission := folderPermissionMap[hs.folderPermissionsService.MapActions(p)]
|
||||||
|
|
||||||
|
acl = append(acl, &dashboards.DashboardACLInfoDTO{
|
||||||
|
OrgID: folder.OrgID,
|
||||||
|
DashboardID: folder.ID,
|
||||||
|
FolderUID: 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: permission,
|
||||||
|
PermissionName: permission.String(),
|
||||||
|
UID: folder.UID,
|
||||||
|
Title: folder.Title,
|
||||||
|
URL: folder.WithURL().URL,
|
||||||
|
IsFolder: true,
|
||||||
|
Inherited: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return acl, nil
|
||||||
|
}
|
||||||
|
|
||||||
// swagger:parameters getFolderPermissionList
|
// swagger:parameters getFolderPermissionList
|
||||||
type GetFolderPermissionListParams struct {
|
type GetFolderPermissionListParams struct {
|
||||||
// in:path
|
// in:path
|
||||||
|
@ -2,373 +2,151 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/api/dtos"
|
|
||||||
"github.com/grafana/grafana/pkg/api/response"
|
|
||||||
"github.com/grafana/grafana/pkg/api/routing"
|
|
||||||
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
service "github.com/grafana/grafana/pkg/services/dashboards/service"
|
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
"github.com/grafana/grafana/pkg/services/folder"
|
||||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||||
"github.com/grafana/grafana/pkg/services/guardian"
|
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
|
"github.com/grafana/grafana/pkg/web/webtest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
func TestHTTPServer_GetFolderPermissionList(t *testing.T) {
|
||||||
settings := setting.NewCfg()
|
t.Run("should not be able to list acl when user does not have permission to do so", func(t *testing.T) {
|
||||||
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {})
|
||||||
|
|
||||||
folderService := &foldertest.FakeService{}
|
res, err := server.Send(webtest.RequestWithSignedInUser(server.NewGetRequest("/api/folders/1/permissions"), userWithPermissions(1, nil)))
|
||||||
|
require.NoError(t, err)
|
||||||
dashboardStore := &dashboards.FakeDashboardStore{}
|
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
||||||
defer dashboardStore.AssertExpectations(t)
|
require.NoError(t, res.Body.Close())
|
||||||
|
|
||||||
features := featuremgmt.WithFeatures()
|
|
||||||
ac := accesscontrolmock.New()
|
|
||||||
folderPermissions := accesscontrolmock.NewMockedPermissionsService()
|
|
||||||
dashboardPermissions := accesscontrolmock.NewMockedPermissionsService()
|
|
||||||
dashboardService, err := service.ProvideDashboardServiceImpl(
|
|
||||||
settings, dashboardStore, foldertest.NewFakeFolderStore(t), nil, features, folderPermissions, dashboardPermissions, ac,
|
|
||||||
folderService,
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
hs := &HTTPServer{
|
|
||||||
Cfg: settings,
|
|
||||||
Features: features,
|
|
||||||
folderService: folderService,
|
|
||||||
folderPermissionsService: folderPermissions,
|
|
||||||
dashboardPermissionsService: dashboardPermissions,
|
|
||||||
DashboardService: dashboardService,
|
|
||||||
AccessControl: ac,
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Given folder not exists", func(t *testing.T) {
|
|
||||||
t.Cleanup(func() {
|
|
||||||
folderService.ExpectedError = nil
|
|
||||||
})
|
|
||||||
folderService.ExpectedError = dashboards.ErrFolderNotFound
|
|
||||||
mockSQLStore := dbtest.NewFakeDB()
|
|
||||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", org.RoleEditor, func(sc *scenarioContext) {
|
|
||||||
callGetFolderPermissions(sc, hs)
|
|
||||||
assert.Equal(t, 404, sc.resp.Code)
|
|
||||||
}, mockSQLStore)
|
|
||||||
|
|
||||||
cmd := dtos.UpdateDashboardACLCommand{
|
|
||||||
Items: []dtos.DashboardACLUpdateItem{
|
|
||||||
{UserID: 1000, Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
updateFolderPermissionScenario(t, updatePermissionContext{
|
|
||||||
desc: "When calling POST on",
|
|
||||||
url: "/api/folders/uid/permissions",
|
|
||||||
routePattern: "/api/folders/:uid/permissions",
|
|
||||||
cmd: cmd,
|
|
||||||
fn: func(sc *scenarioContext) {
|
|
||||||
callUpdateFolderPermissions(t, sc)
|
|
||||||
assert.Equal(t, 404, sc.resp.Code)
|
|
||||||
},
|
|
||||||
}, hs)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Given user has no admin permissions", func(t *testing.T) {
|
t.Run("should be able to list acl with correct permission", func(t *testing.T) {
|
||||||
origNewGuardian := guardian.New
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
t.Cleanup(func() {
|
hs.folderService = &foldertest.FakeService{ExpectedFolder: &folder.Folder{ID: 1, UID: "1"}}
|
||||||
guardian.New = origNewGuardian
|
hs.folderPermissionsService = &actest.FakePermissionsService{
|
||||||
folderService.ExpectedError = nil
|
ExpectedPermissions: []accesscontrol.ResourcePermission{},
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanAdminValue: false})
|
res, err := server.Send(webtest.RequestWithSignedInUser(server.NewGetRequest("/api/folders/1/permissions"), userWithPermissions(1, []accesscontrol.Permission{
|
||||||
folderService.ExpectedError = dashboards.ErrFolderAccessDenied
|
{Action: dashboards.ActionFoldersPermissionsRead, Scope: "folders:uid:1"},
|
||||||
mockSQLStore := dbtest.NewFakeDB()
|
})))
|
||||||
|
|
||||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", org.RoleEditor, func(sc *scenarioContext) {
|
require.NoError(t, err)
|
||||||
callGetFolderPermissions(sc, hs)
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
assert.Equal(t, 403, sc.resp.Code)
|
require.NoError(t, res.Body.Close())
|
||||||
}, mockSQLStore)
|
|
||||||
|
|
||||||
cmd := dtos.UpdateDashboardACLCommand{
|
|
||||||
Items: []dtos.DashboardACLUpdateItem{
|
|
||||||
{UserID: 1000, Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
updateFolderPermissionScenario(t, updatePermissionContext{
|
|
||||||
desc: "When calling POST on",
|
|
||||||
url: "/api/folders/uid/permissions",
|
|
||||||
routePattern: "/api/folders/:uid/permissions",
|
|
||||||
cmd: cmd,
|
|
||||||
fn: func(sc *scenarioContext) {
|
|
||||||
callUpdateFolderPermissions(t, sc)
|
|
||||||
assert.Equal(t, 403, sc.resp.Code)
|
|
||||||
},
|
|
||||||
}, hs)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Given user has admin permissions and permissions to update", func(t *testing.T) {
|
t.Run("should filter out hidden users from acl", func(t *testing.T) {
|
||||||
origNewGuardian := guardian.New
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
t.Cleanup(func() {
|
cfg := setting.NewCfg()
|
||||||
guardian.New = origNewGuardian
|
cfg.HiddenUsers = map[string]struct{}{"hidden": {}}
|
||||||
})
|
hs.Cfg = cfg
|
||||||
|
hs.folderService = &foldertest.FakeService{ExpectedFolder: &folder.Folder{ID: 1, UID: "1"}}
|
||||||
|
|
||||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
|
hs.folderPermissionsService = &actest.FakePermissionsService{
|
||||||
CanAdminValue: true,
|
ExpectedPermissions: []accesscontrol.ResourcePermission{
|
||||||
CheckPermissionBeforeUpdateValue: true,
|
{UserId: 1, UserLogin: "regular", IsManaged: true},
|
||||||
GetACLValue: []*dashboards.DashboardACLInfoDTO{
|
{UserId: 2, UserLogin: "hidden", IsManaged: true},
|
||||||
{OrgID: 1, DashboardID: 1, UserID: 2, Permission: dashboards.PERMISSION_VIEW},
|
|
||||||
{OrgID: 1, DashboardID: 1, UserID: 3, Permission: dashboards.PERMISSION_EDIT},
|
|
||||||
{OrgID: 1, DashboardID: 1, UserID: 4, Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
{OrgID: 1, DashboardID: 1, TeamID: 1, Permission: dashboards.PERMISSION_VIEW},
|
|
||||||
{OrgID: 1, DashboardID: 1, TeamID: 2, Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
folderService.ExpectedFolder = &folder.Folder{ID: 1, UID: "uid", Title: "Folder"}
|
|
||||||
mockSQLStore := dbtest.NewFakeDB()
|
|
||||||
|
|
||||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", org.RoleAdmin, func(sc *scenarioContext) {
|
|
||||||
callGetFolderPermissions(sc, hs)
|
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
|
||||||
|
|
||||||
var resp []*dashboards.DashboardACLInfoDTO
|
|
||||||
err := json.Unmarshal(sc.resp.Body.Bytes(), &resp)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Len(t, resp, 5)
|
|
||||||
assert.Equal(t, int64(2), resp[0].UserID)
|
|
||||||
assert.Equal(t, dashboards.PERMISSION_VIEW, resp[0].Permission)
|
|
||||||
}, mockSQLStore)
|
|
||||||
|
|
||||||
cmd := dtos.UpdateDashboardACLCommand{
|
|
||||||
Items: []dtos.DashboardACLUpdateItem{
|
|
||||||
{UserID: 1000, Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
folderPermissions.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil).Once()
|
|
||||||
updateFolderPermissionScenario(t, updatePermissionContext{
|
|
||||||
desc: "When calling POST on",
|
|
||||||
url: "/api/folders/uid/permissions",
|
|
||||||
routePattern: "/api/folders/:uid/permissions",
|
|
||||||
cmd: cmd,
|
|
||||||
fn: func(sc *scenarioContext) {
|
|
||||||
callUpdateFolderPermissions(t, sc)
|
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
|
||||||
|
|
||||||
var resp struct {
|
|
||||||
Message string
|
|
||||||
}
|
|
||||||
err := json.Unmarshal(sc.resp.Body.Bytes(), &resp)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, "Folder permissions updated", resp.Message)
|
|
||||||
},
|
|
||||||
}, hs)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("When trying to update permissions with duplicate permissions", func(t *testing.T) {
|
|
||||||
origNewGuardian := guardian.New
|
|
||||||
t.Cleanup(func() {
|
|
||||||
guardian.New = origNewGuardian
|
|
||||||
})
|
|
||||||
|
|
||||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
|
|
||||||
CanAdminValue: true,
|
|
||||||
CheckPermissionBeforeUpdateValue: false,
|
|
||||||
CheckPermissionBeforeUpdateError: guardian.ErrGuardianPermissionExists,
|
|
||||||
})
|
|
||||||
|
|
||||||
folderService.ExpectedFolder = &folder.Folder{ID: 1, UID: "uid", Title: "Folder"}
|
|
||||||
|
|
||||||
cmd := dtos.UpdateDashboardACLCommand{
|
|
||||||
Items: []dtos.DashboardACLUpdateItem{
|
|
||||||
{UserID: 1000, Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
updateFolderPermissionScenario(t, updatePermissionContext{
|
|
||||||
desc: "When calling POST on",
|
|
||||||
url: "/api/folders/uid/permissions",
|
|
||||||
routePattern: "/api/folders/:uid/permissions",
|
|
||||||
cmd: cmd,
|
|
||||||
fn: func(sc *scenarioContext) {
|
|
||||||
callUpdateFolderPermissions(t, sc)
|
|
||||||
assert.Equal(t, 400, sc.resp.Code)
|
|
||||||
},
|
|
||||||
}, hs)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("When trying to update team or user permissions with a role", func(t *testing.T) {
|
|
||||||
role := org.RoleAdmin
|
|
||||||
cmds := []dtos.UpdateDashboardACLCommand{
|
|
||||||
{
|
|
||||||
Items: []dtos.DashboardACLUpdateItem{
|
|
||||||
{UserID: 1000, Permission: dashboards.PERMISSION_ADMIN, Role: &role},
|
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
{
|
|
||||||
Items: []dtos.DashboardACLUpdateItem{
|
|
||||||
{TeamID: 1000, Permission: dashboards.PERMISSION_ADMIN, Role: &role},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, cmd := range cmds {
|
|
||||||
updateFolderPermissionScenario(t, updatePermissionContext{
|
|
||||||
desc: "When calling POST on",
|
|
||||||
url: "/api/folders/uid/permissions",
|
|
||||||
routePattern: "/api/folders/:uid/permissions",
|
|
||||||
cmd: cmd,
|
|
||||||
fn: func(sc *scenarioContext) {
|
|
||||||
callUpdateFolderPermissions(t, sc)
|
|
||||||
assert.Equal(t, 400, sc.resp.Code)
|
|
||||||
respJSON, err := jsonMap(sc.resp.Body.Bytes())
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, dashboards.ErrPermissionsWithRoleNotAllowed.Error(), respJSON["error"])
|
|
||||||
},
|
|
||||||
}, hs)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("When trying to override inherited permissions with lower precedence", func(t *testing.T) {
|
|
||||||
origNewGuardian := guardian.New
|
|
||||||
t.Cleanup(func() {
|
|
||||||
guardian.New = origNewGuardian
|
|
||||||
})
|
})
|
||||||
|
|
||||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
|
res, err := server.Send(webtest.RequestWithSignedInUser(server.NewGetRequest("/api/folders/1/permissions"), userWithPermissions(1, []accesscontrol.Permission{
|
||||||
CanAdminValue: true,
|
{Action: dashboards.ActionFoldersPermissionsRead, Scope: "folders:uid:1"},
|
||||||
CheckPermissionBeforeUpdateValue: false,
|
})))
|
||||||
CheckPermissionBeforeUpdateError: guardian.ErrGuardianOverride},
|
|
||||||
)
|
|
||||||
|
|
||||||
folderService.ExpectedFolder = &folder.Folder{ID: 1, UID: "uid", Title: "Folder"}
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
|
|
||||||
cmd := dtos.UpdateDashboardACLCommand{
|
var result []dashboards.DashboardACLInfoDTO
|
||||||
Items: []dtos.DashboardACLUpdateItem{
|
require.NoError(t, json.NewDecoder(res.Body).Decode(&result))
|
||||||
{UserID: 1000, Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
updateFolderPermissionScenario(t, updatePermissionContext{
|
assert.Len(t, result, 1)
|
||||||
desc: "When calling POST on",
|
assert.Equal(t, result[0].UserLogin, "regular")
|
||||||
url: "/api/folders/uid/permissions",
|
require.NoError(t, res.Body.Close())
|
||||||
routePattern: "/api/folders/:uid/permissions",
|
|
||||||
cmd: cmd,
|
|
||||||
fn: func(sc *scenarioContext) {
|
|
||||||
callUpdateFolderPermissions(t, sc)
|
|
||||||
assert.Equal(t, 400, sc.resp.Code)
|
|
||||||
},
|
|
||||||
}, hs)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Getting and updating folder permissions with hidden users", func(t *testing.T) {
|
|
||||||
origNewGuardian := guardian.New
|
|
||||||
settings.HiddenUsers = map[string]struct{}{
|
|
||||||
"hiddenUser": {},
|
|
||||||
testUserLogin: {},
|
|
||||||
}
|
|
||||||
t.Cleanup(func() {
|
|
||||||
guardian.New = origNewGuardian
|
|
||||||
settings.HiddenUsers = make(map[string]struct{})
|
|
||||||
})
|
|
||||||
|
|
||||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
|
|
||||||
CanAdminValue: true,
|
|
||||||
CheckPermissionBeforeUpdateValue: true,
|
|
||||||
GetACLValue: []*dashboards.DashboardACLInfoDTO{
|
|
||||||
{OrgID: 1, DashboardID: 1, UserID: 2, UserLogin: "hiddenUser", Permission: dashboards.PERMISSION_VIEW},
|
|
||||||
{OrgID: 1, DashboardID: 1, UserID: 3, UserLogin: testUserLogin, Permission: dashboards.PERMISSION_EDIT},
|
|
||||||
{OrgID: 1, DashboardID: 1, UserID: 4, UserLogin: "user_1", Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
},
|
|
||||||
GetHiddenACLValue: []*dashboards.DashboardACL{
|
|
||||||
{OrgID: 1, DashboardID: 1, UserID: 2, Permission: dashboards.PERMISSION_VIEW},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
folderService.ExpectedFolder = &folder.Folder{ID: 1, UID: "uid", Title: "Folder"}
|
|
||||||
|
|
||||||
var resp []*dashboards.DashboardACLInfoDTO
|
|
||||||
mockSQLStore := dbtest.NewFakeDB()
|
|
||||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", org.RoleAdmin, func(sc *scenarioContext) {
|
|
||||||
callGetFolderPermissions(sc, hs)
|
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
|
||||||
|
|
||||||
err := json.Unmarshal(sc.resp.Body.Bytes(), &resp)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Len(t, resp, 2)
|
|
||||||
assert.Equal(t, int64(3), resp[0].UserID)
|
|
||||||
assert.Equal(t, dashboards.PERMISSION_EDIT, resp[0].Permission)
|
|
||||||
assert.Equal(t, int64(4), resp[1].UserID)
|
|
||||||
assert.Equal(t, dashboards.PERMISSION_ADMIN, resp[1].Permission)
|
|
||||||
}, mockSQLStore)
|
|
||||||
|
|
||||||
cmd := dtos.UpdateDashboardACLCommand{
|
|
||||||
Items: []dtos.DashboardACLUpdateItem{
|
|
||||||
{UserID: 1000, Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, acl := range resp {
|
|
||||||
cmd.Items = append(cmd.Items, dtos.DashboardACLUpdateItem{
|
|
||||||
UserID: acl.UserID,
|
|
||||||
Permission: acl.Permission,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
assert.Len(t, cmd.Items, 3)
|
|
||||||
|
|
||||||
folderPermissions.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil).Once()
|
|
||||||
updateFolderPermissionScenario(t, updatePermissionContext{
|
|
||||||
desc: "When calling POST on",
|
|
||||||
url: "/api/folders/uid/permissions",
|
|
||||||
routePattern: "/api/folders/:uid/permissions",
|
|
||||||
cmd: cmd,
|
|
||||||
fn: func(sc *scenarioContext) {
|
|
||||||
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
|
||||||
assert.Equal(t, 200, sc.resp.Code)
|
|
||||||
},
|
|
||||||
}, hs)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func callGetFolderPermissions(sc *scenarioContext, hs *HTTPServer) {
|
func TestHTTPServer_UpdateFolderPermissions(t *testing.T) {
|
||||||
sc.handlerFunc = hs.GetFolderPermissionList
|
t.Run("should not be able to update acl when user does not have permission to do so", func(t *testing.T) {
|
||||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {})
|
||||||
}
|
|
||||||
|
|
||||||
func callUpdateFolderPermissions(t *testing.T, sc *scenarioContext) {
|
res, err := server.Send(webtest.RequestWithSignedInUser(server.NewPostRequest("/api/folders/1/permissions", nil), userWithPermissions(1, nil)))
|
||||||
t.Helper()
|
require.NoError(t, err)
|
||||||
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
||||||
}
|
require.NoError(t, res.Body.Close())
|
||||||
|
})
|
||||||
|
|
||||||
func updateFolderPermissionScenario(t *testing.T, ctx updatePermissionContext, hs *HTTPServer) {
|
t.Run("should be able to update acl with correct permissions", func(t *testing.T) {
|
||||||
t.Run(fmt.Sprintf("%s %s", ctx.desc, ctx.url), func(t *testing.T) {
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
sc := setupScenarioContext(t, ctx.url)
|
hs.folderService = &foldertest.FakeService{ExpectedFolder: &folder.Folder{ID: 1, UID: "1"}}
|
||||||
|
hs.folderPermissionsService = &actest.FakePermissionsService{}
|
||||||
sc.defaultHandler = routing.Wrap(func(c *contextmodel.ReqContext) response.Response {
|
|
||||||
c.Req.Body = mockRequestBody(ctx.cmd)
|
|
||||||
c.Req.Header.Add("Content-Type", "application/json")
|
|
||||||
sc.context = c
|
|
||||||
sc.context.OrgID = testOrgID
|
|
||||||
sc.context.UserID = testUserID
|
|
||||||
|
|
||||||
return hs.UpdateFolderPermissions(c)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
sc.m.Post(ctx.routePattern, sc.defaultHandler)
|
body := `{"items": []}`
|
||||||
|
res, err := server.SendJSON(webtest.RequestWithSignedInUser(server.NewPostRequest("/api/folders/1/permissions", strings.NewReader(body)), userWithPermissions(1, []accesscontrol.Permission{
|
||||||
|
{Action: dashboards.ActionFoldersPermissionsWrite, Scope: "folders:uid:1"},
|
||||||
|
})))
|
||||||
|
|
||||||
ctx.fn(sc)
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
|
require.NoError(t, res.Body.Close())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should not be able to specify team and user in same acl", func(t *testing.T) {
|
||||||
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
|
hs.folderService = &foldertest.FakeService{ExpectedFolder: &folder.Folder{ID: 1, UID: "1"}}
|
||||||
|
hs.folderPermissionsService = &actest.FakePermissionsService{}
|
||||||
|
})
|
||||||
|
|
||||||
|
body := `{"items": [{ userId:1, teamId: 2 }]}`
|
||||||
|
res, err := server.SendJSON(webtest.RequestWithSignedInUser(server.NewPostRequest("/api/folders/1/permissions", strings.NewReader(body)), userWithPermissions(1, []accesscontrol.Permission{
|
||||||
|
{Action: dashboards.ActionFoldersPermissionsWrite, Scope: "folders:uid:1"},
|
||||||
|
})))
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, http.StatusBadRequest, res.StatusCode)
|
||||||
|
require.NoError(t, res.Body.Close())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should not be able to specify team and role in same acl", func(t *testing.T) {
|
||||||
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
|
hs.folderService = &foldertest.FakeService{ExpectedFolder: &folder.Folder{ID: 1, UID: "1"}}
|
||||||
|
hs.folderPermissionsService = &actest.FakePermissionsService{}
|
||||||
|
})
|
||||||
|
|
||||||
|
body := `{"items": [{ teamId:1, role: "Admin" }]}`
|
||||||
|
res, err := server.SendJSON(webtest.RequestWithSignedInUser(server.NewPostRequest("/api/folders/1/permissions", strings.NewReader(body)), userWithPermissions(1, []accesscontrol.Permission{
|
||||||
|
{Action: dashboards.ActionFoldersPermissionsWrite, Scope: "folders:uid:1"},
|
||||||
|
})))
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, http.StatusBadRequest, res.StatusCode)
|
||||||
|
require.NoError(t, res.Body.Close())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should not be able to specify user and role in same acl", func(t *testing.T) {
|
||||||
|
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||||
|
hs.folderService = &foldertest.FakeService{ExpectedFolder: &folder.Folder{ID: 1, UID: "1"}}
|
||||||
|
hs.folderPermissionsService = &actest.FakePermissionsService{}
|
||||||
|
})
|
||||||
|
|
||||||
|
body := `{"items": [{ userId:1, role: "Admin" }]}`
|
||||||
|
res, err := server.SendJSON(webtest.RequestWithSignedInUser(server.NewPostRequest("/api/folders/1/permissions", strings.NewReader(body)), userWithPermissions(1, []accesscontrol.Permission{
|
||||||
|
{Action: dashboards.ActionFoldersPermissionsWrite, Scope: "folders:uid:1"},
|
||||||
|
})))
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, http.StatusBadRequest, res.StatusCode)
|
||||||
|
require.NoError(t, res.Body.Close())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"net/mail"
|
"net/mail"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/middleware/cookies"
|
"github.com/grafana/grafana/pkg/middleware/cookies"
|
||||||
@ -21,12 +20,6 @@ func (hs *HTTPServer) GetRedirectURL(c *contextmodel.ReqContext) string {
|
|||||||
return redirectURL
|
return redirectURL
|
||||||
}
|
}
|
||||||
|
|
||||||
func jsonMap(data []byte) (map[string]string, error) {
|
|
||||||
jsonMap := make(map[string]string)
|
|
||||||
err := json.Unmarshal(data, &jsonMap)
|
|
||||||
return jsonMap, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func ValidateAndNormalizeEmail(email string) (string, error) {
|
func ValidateAndNormalizeEmail(email string) (string, error) {
|
||||||
if email == "" {
|
if email == "" {
|
||||||
return "", nil
|
return "", nil
|
||||||
|
@ -9,17 +9,10 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/folder"
|
"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/services/user"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"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)
|
var _ DashboardGuardian = new(accessControlDashboardGuardian)
|
||||||
|
|
||||||
// NewAccessControlDashboardGuardianByDashboard creates a dashboard guardian by the provided dashboardId.
|
// NewAccessControlDashboardGuardianByDashboard creates a dashboard guardian by the provided dashboardId.
|
||||||
@ -390,189 +383,6 @@ func (a *accessControlFolderGuardian) evaluate(evaluator accesscontrol.Evaluator
|
|||||||
return ok, err
|
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) {
|
|
||||||
if a.dashboard == nil {
|
|
||||||
return nil, ErrGuardianGetDashboardFailure.Errorf("failed to translate access control permissions to dashboard acl info")
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 *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
|
|
||||||
}
|
|
||||||
|
|
||||||
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 *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) {
|
func (a *accessControlDashboardGuardian) loadParentFolder(folderID int64) (*dashboards.Dashboard, error) {
|
||||||
if folderID == 0 {
|
if folderID == 0 {
|
||||||
return &dashboards.Dashboard{UID: accesscontrol.GeneralFolderUID}, nil
|
return &dashboards.Dashboard{UID: accesscontrol.GeneralFolderUID}, nil
|
||||||
|
@ -15,7 +15,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
||||||
acdb "github.com/grafana/grafana/pkg/services/accesscontrol/database"
|
acdb "github.com/grafana/grafana/pkg/services/accesscontrol/database"
|
||||||
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol/ossaccesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||||
@ -31,6 +30,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
orgID = 1
|
||||||
dashUID = "1"
|
dashUID = "1"
|
||||||
folderID = 42
|
folderID = 42
|
||||||
folderUID = "42"
|
folderUID = "42"
|
||||||
@ -1015,63 +1015,6 @@ func TestAccessControlDashboardGuardian_CanCreate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type accessControlGuardianGetHiddenACLTestCase struct {
|
|
||||||
desc string
|
|
||||||
permissions []accesscontrol.ResourcePermission
|
|
||||||
hiddenUsers map[string]struct{}
|
|
||||||
isFolder bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAccessControlDashboardGuardian_GetHiddenACL(t *testing.T) {
|
|
||||||
tests := []accessControlGuardianGetHiddenACLTestCase{
|
|
||||||
{
|
|
||||||
desc: "should only return permissions containing hidden users",
|
|
||||||
permissions: []accesscontrol.ResourcePermission{
|
|
||||||
{RoleName: "managed:users:1:permissions", UserId: 1, UserLogin: "user1", IsManaged: true},
|
|
||||||
{RoleName: "managed:teams:1:permissions", TeamId: 1, Team: "team1", IsManaged: true},
|
|
||||||
{RoleName: "managed:users:2:permissions", UserId: 2, UserLogin: "user2", IsManaged: true},
|
|
||||||
{RoleName: "managed:users:3:permissions", UserId: 3, UserLogin: "user3", IsManaged: true},
|
|
||||||
{RoleName: "managed:users:4:permissions", UserId: 4, UserLogin: "user4", IsManaged: true},
|
|
||||||
},
|
|
||||||
hiddenUsers: map[string]struct{}{"user2": {}, "user3": {}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "should only return permissions containing hidden users",
|
|
||||||
permissions: []accesscontrol.ResourcePermission{
|
|
||||||
{RoleName: "managed:users:1:permissions", UserId: 1, UserLogin: "user1", IsManaged: true},
|
|
||||||
{RoleName: "managed:teams:1:permissions", TeamId: 1, Team: "team1", IsManaged: true},
|
|
||||||
{RoleName: "managed:users:2:permissions", UserId: 2, UserLogin: "user2", IsManaged: true},
|
|
||||||
{RoleName: "managed:users:3:permissions", UserId: 3, UserLogin: "user3", IsManaged: true},
|
|
||||||
{RoleName: "managed:users:4:permissions", UserId: 4, UserLogin: "user4", IsManaged: true},
|
|
||||||
},
|
|
||||||
hiddenUsers: map[string]struct{}{"user2": {}, "user3": {}},
|
|
||||||
isFolder: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.desc, func(t *testing.T) {
|
|
||||||
mocked := accesscontrolmock.NewMockedPermissionsService()
|
|
||||||
mocked.On("MapActions", mock.Anything).Return("View")
|
|
||||||
mocked.On("GetPermissions", mock.Anything, mock.Anything, mock.Anything).Return(tt.permissions, nil)
|
|
||||||
guardian := setupAccessControlGuardianTest(t, &dashboards.Dashboard{OrgID: orgID, UID: "1", IsFolder: tt.isFolder}, nil, nil, mocked, mocked)
|
|
||||||
|
|
||||||
cfg := setting.NewCfg()
|
|
||||||
cfg.HiddenUsers = tt.hiddenUsers
|
|
||||||
permissions, err := guardian.GetHiddenACL(cfg)
|
|
||||||
require.NoError(t, err)
|
|
||||||
var hiddenUserNames []string
|
|
||||||
for name := range tt.hiddenUsers {
|
|
||||||
hiddenUserNames = append(hiddenUserNames, name)
|
|
||||||
}
|
|
||||||
assert.Len(t, permissions, len(hiddenUserNames))
|
|
||||||
for _, p := range permissions {
|
|
||||||
assert.Contains(t, hiddenUserNames, fmt.Sprintf("user%d", p.UserID))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupAccessControlGuardianTest(t *testing.T, d *dashboards.Dashboard,
|
func setupAccessControlGuardianTest(t *testing.T, d *dashboards.Dashboard,
|
||||||
permissions []accesscontrol.Permission,
|
permissions []accesscontrol.Permission,
|
||||||
cfg *setting.Cfg,
|
cfg *setting.Cfg,
|
||||||
|
@ -32,16 +32,6 @@ type DashboardGuardian interface {
|
|||||||
CanAdmin() (bool, error)
|
CanAdmin() (bool, error)
|
||||||
CanDelete() (bool, error)
|
CanDelete() (bool, error)
|
||||||
CanCreate(folderID int64, isFolder bool) (bool, error)
|
CanCreate(folderID int64, isFolder bool) (bool, error)
|
||||||
CheckPermissionBeforeUpdate(permission dashboards.PermissionType, updatePermissions []*dashboards.DashboardACL) (bool, error)
|
|
||||||
|
|
||||||
// GetACL returns ACL.
|
|
||||||
GetACL() ([]*dashboards.DashboardACLInfoDTO, error)
|
|
||||||
|
|
||||||
// GetACLWithoutDuplicates returns ACL and strips any permission
|
|
||||||
// that already has an inherited permission with higher or equal
|
|
||||||
// permission.
|
|
||||||
GetACLWithoutDuplicates() ([]*dashboards.DashboardACLInfoDTO, error)
|
|
||||||
GetHiddenACL(*setting.Cfg) ([]*dashboards.DashboardACL, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type dashboardGuardianImpl struct {
|
type dashboardGuardianImpl struct {
|
||||||
@ -470,22 +460,6 @@ func (g *FakeDashboardGuardian) HasPermission(permission dashboards.PermissionTy
|
|||||||
return g.HasPermissionValue, nil
|
return g.HasPermissionValue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *FakeDashboardGuardian) CheckPermissionBeforeUpdate(permission dashboards.PermissionType, updatePermissions []*dashboards.DashboardACL) (bool, error) {
|
|
||||||
return g.CheckPermissionBeforeUpdateValue, g.CheckPermissionBeforeUpdateError
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *FakeDashboardGuardian) GetACL() ([]*dashboards.DashboardACLInfoDTO, error) {
|
|
||||||
return g.GetACLValue, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *FakeDashboardGuardian) GetACLWithoutDuplicates() ([]*dashboards.DashboardACLInfoDTO, error) {
|
|
||||||
return g.GetACL()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *FakeDashboardGuardian) GetHiddenACL(cfg *setting.Cfg) ([]*dashboards.DashboardACL, error) {
|
|
||||||
return g.GetHiddenACLValue, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint:unused
|
// nolint:unused
|
||||||
func MockDashboardGuardian(mock *FakeDashboardGuardian) {
|
func MockDashboardGuardian(mock *FakeDashboardGuardian) {
|
||||||
New = func(_ context.Context, dashID int64, orgId int64, user *user.SignedInUser) (DashboardGuardian, error) {
|
New = func(_ context.Context, dashID int64, orgId int64, user *user.SignedInUser) (DashboardGuardian, error) {
|
||||||
|
@ -1,797 +0,0 @@
|
|||||||
package guardian
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
|
||||||
"github.com/grafana/grafana/pkg/services/team/teamtest"
|
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
orgID = int64(1)
|
|
||||||
defaultDashboardID = int64(-1)
|
|
||||||
dashboardID = int64(1)
|
|
||||||
dashboardUID = "uid"
|
|
||||||
parentFolderID = int64(2)
|
|
||||||
childDashboardID = int64(3)
|
|
||||||
userID = int64(1)
|
|
||||||
otherUserID = int64(2)
|
|
||||||
teamID = int64(1)
|
|
||||||
otherTeamID = int64(2)
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
adminRole = org.RoleAdmin
|
|
||||||
editorRole = org.RoleEditor
|
|
||||||
viewerRole = org.RoleViewer
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGuardianAdmin(t *testing.T) {
|
|
||||||
orgRoleScenario("Given user has admin org role", t, org.RoleAdmin, func(sc *scenarioContext) {
|
|
||||||
// dashboard has default permissions
|
|
||||||
sc.defaultPermissionScenario(USER, FULL_ACCESS)
|
|
||||||
|
|
||||||
// dashboard has user with permission
|
|
||||||
sc.dashboardPermissionScenario(USER, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(USER, dashboards.PERMISSION_EDIT, FULL_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(USER, dashboards.PERMISSION_VIEW, FULL_ACCESS)
|
|
||||||
|
|
||||||
// dashboard has team with permission
|
|
||||||
sc.dashboardPermissionScenario(TEAM, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(TEAM, dashboards.PERMISSION_EDIT, FULL_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(TEAM, dashboards.PERMISSION_VIEW, FULL_ACCESS)
|
|
||||||
|
|
||||||
// dashboard has editor role with permission
|
|
||||||
sc.dashboardPermissionScenario(EDITOR, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(EDITOR, dashboards.PERMISSION_EDIT, FULL_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(EDITOR, dashboards.PERMISSION_VIEW, FULL_ACCESS)
|
|
||||||
|
|
||||||
// dashboard has viewer role with permission
|
|
||||||
sc.dashboardPermissionScenario(VIEWER, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(VIEWER, dashboards.PERMISSION_EDIT, FULL_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(VIEWER, dashboards.PERMISSION_VIEW, FULL_ACCESS)
|
|
||||||
|
|
||||||
// parent folder has user with permission
|
|
||||||
sc.parentFolderPermissionScenario(USER, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(USER, dashboards.PERMISSION_EDIT, FULL_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(USER, dashboards.PERMISSION_VIEW, FULL_ACCESS)
|
|
||||||
|
|
||||||
// parent folder has team with permission
|
|
||||||
sc.parentFolderPermissionScenario(TEAM, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(TEAM, dashboards.PERMISSION_EDIT, FULL_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(TEAM, dashboards.PERMISSION_VIEW, FULL_ACCESS)
|
|
||||||
|
|
||||||
// parent folder has editor role with permission
|
|
||||||
sc.parentFolderPermissionScenario(EDITOR, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(EDITOR, dashboards.PERMISSION_EDIT, FULL_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(EDITOR, dashboards.PERMISSION_VIEW, FULL_ACCESS)
|
|
||||||
|
|
||||||
// parent folder has viewer role with permission
|
|
||||||
sc.parentFolderPermissionScenario(VIEWER, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(VIEWER, dashboards.PERMISSION_EDIT, FULL_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(VIEWER, dashboards.PERMISSION_VIEW, FULL_ACCESS)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGuardianEditor(t *testing.T) {
|
|
||||||
orgRoleScenario("Given user has editor org role", t, org.RoleEditor, func(sc *scenarioContext) {
|
|
||||||
// dashboard has default permissions
|
|
||||||
sc.defaultPermissionScenario(USER, EDITOR_ACCESS)
|
|
||||||
|
|
||||||
// dashboard has user with permission
|
|
||||||
sc.dashboardPermissionScenario(USER, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(USER, dashboards.PERMISSION_EDIT, EDITOR_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(USER, dashboards.PERMISSION_VIEW, CAN_VIEW)
|
|
||||||
|
|
||||||
// dashboard has team with permission
|
|
||||||
sc.dashboardPermissionScenario(TEAM, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(TEAM, dashboards.PERMISSION_EDIT, EDITOR_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(TEAM, dashboards.PERMISSION_VIEW, CAN_VIEW)
|
|
||||||
|
|
||||||
// dashboard has editor role with permission
|
|
||||||
sc.dashboardPermissionScenario(EDITOR, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(EDITOR, dashboards.PERMISSION_EDIT, EDITOR_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(EDITOR, dashboards.PERMISSION_VIEW, VIEWER_ACCESS)
|
|
||||||
|
|
||||||
// dashboard has viewer role with permission
|
|
||||||
sc.dashboardPermissionScenario(VIEWER, dashboards.PERMISSION_ADMIN, NO_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(VIEWER, dashboards.PERMISSION_EDIT, NO_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(VIEWER, dashboards.PERMISSION_VIEW, NO_ACCESS)
|
|
||||||
|
|
||||||
// parent folder has user with permission
|
|
||||||
sc.parentFolderPermissionScenario(USER, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(USER, dashboards.PERMISSION_EDIT, EDITOR_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(USER, dashboards.PERMISSION_VIEW, VIEWER_ACCESS)
|
|
||||||
|
|
||||||
// parent folder has team with permission
|
|
||||||
sc.parentFolderPermissionScenario(TEAM, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(TEAM, dashboards.PERMISSION_EDIT, EDITOR_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(TEAM, dashboards.PERMISSION_VIEW, VIEWER_ACCESS)
|
|
||||||
|
|
||||||
// parent folder has editor role with permission
|
|
||||||
sc.parentFolderPermissionScenario(EDITOR, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(EDITOR, dashboards.PERMISSION_EDIT, EDITOR_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(EDITOR, dashboards.PERMISSION_VIEW, VIEWER_ACCESS)
|
|
||||||
|
|
||||||
// parent folder has viewer role with permission
|
|
||||||
sc.parentFolderPermissionScenario(VIEWER, dashboards.PERMISSION_ADMIN, NO_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(VIEWER, dashboards.PERMISSION_EDIT, NO_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(VIEWER, dashboards.PERMISSION_VIEW, NO_ACCESS)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGuardianViewer(t *testing.T) {
|
|
||||||
orgRoleScenario("Given user has viewer org role", t, org.RoleViewer, func(sc *scenarioContext) {
|
|
||||||
// dashboard has default permissions
|
|
||||||
sc.defaultPermissionScenario(USER, VIEWER_ACCESS)
|
|
||||||
|
|
||||||
// dashboard has user with permission
|
|
||||||
sc.dashboardPermissionScenario(USER, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(USER, dashboards.PERMISSION_EDIT, EDITOR_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(USER, dashboards.PERMISSION_VIEW, VIEWER_ACCESS)
|
|
||||||
|
|
||||||
// dashboard has team with permission
|
|
||||||
sc.dashboardPermissionScenario(TEAM, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(TEAM, dashboards.PERMISSION_EDIT, EDITOR_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(TEAM, dashboards.PERMISSION_VIEW, VIEWER_ACCESS)
|
|
||||||
|
|
||||||
// dashboard has editor role with permission
|
|
||||||
sc.dashboardPermissionScenario(EDITOR, dashboards.PERMISSION_ADMIN, NO_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(EDITOR, dashboards.PERMISSION_EDIT, NO_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(EDITOR, dashboards.PERMISSION_VIEW, NO_ACCESS)
|
|
||||||
|
|
||||||
// dashboard has viewer role with permission
|
|
||||||
sc.dashboardPermissionScenario(VIEWER, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(VIEWER, dashboards.PERMISSION_EDIT, EDITOR_ACCESS)
|
|
||||||
sc.dashboardPermissionScenario(VIEWER, dashboards.PERMISSION_VIEW, VIEWER_ACCESS)
|
|
||||||
|
|
||||||
// parent folder has user with permission
|
|
||||||
sc.parentFolderPermissionScenario(USER, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(USER, dashboards.PERMISSION_EDIT, EDITOR_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(USER, dashboards.PERMISSION_VIEW, VIEWER_ACCESS)
|
|
||||||
|
|
||||||
// parent folder has team with permission
|
|
||||||
sc.parentFolderPermissionScenario(TEAM, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(TEAM, dashboards.PERMISSION_EDIT, EDITOR_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(TEAM, dashboards.PERMISSION_VIEW, VIEWER_ACCESS)
|
|
||||||
|
|
||||||
// parent folder has editor role with permission
|
|
||||||
sc.parentFolderPermissionScenario(EDITOR, dashboards.PERMISSION_ADMIN, NO_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(EDITOR, dashboards.PERMISSION_EDIT, NO_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(EDITOR, dashboards.PERMISSION_VIEW, NO_ACCESS)
|
|
||||||
|
|
||||||
// parent folder has viewer role with permission
|
|
||||||
sc.parentFolderPermissionScenario(VIEWER, dashboards.PERMISSION_ADMIN, FULL_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(VIEWER, dashboards.PERMISSION_EDIT, EDITOR_ACCESS)
|
|
||||||
sc.parentFolderPermissionScenario(VIEWER, dashboards.PERMISSION_VIEW, VIEWER_ACCESS)
|
|
||||||
})
|
|
||||||
|
|
||||||
apiKeyScenario("Given api key with viewer role", t, org.RoleViewer, func(sc *scenarioContext) {
|
|
||||||
// dashboard has default permissions
|
|
||||||
sc.defaultPermissionScenario(VIEWER, VIEWER_ACCESS)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *scenarioContext) defaultPermissionScenario(pt permissionType, flag permissionFlags) {
|
|
||||||
_, callerFile, callerLine, _ := runtime.Caller(1)
|
|
||||||
sc.callerFile = callerFile
|
|
||||||
sc.callerLine = callerLine
|
|
||||||
existingPermissions := []*dashboards.DashboardACLInfoDTO{
|
|
||||||
toDto(newEditorRolePermission(defaultDashboardID, dashboards.PERMISSION_EDIT)),
|
|
||||||
toDto(newViewerRolePermission(defaultDashboardID, dashboards.PERMISSION_VIEW)),
|
|
||||||
}
|
|
||||||
|
|
||||||
permissionScenario("and existing permissions are the default permissions (everyone with editor role can edit, everyone with viewer role can view)",
|
|
||||||
dashboardID, sc, existingPermissions, func(sc *scenarioContext) {
|
|
||||||
sc.expectedFlags = flag
|
|
||||||
sc.verifyExpectedPermissionsFlags()
|
|
||||||
sc.verifyDuplicatePermissionsShouldNotBeAllowed()
|
|
||||||
sc.verifyUpdateDashboardPermissionsShouldBeAllowed(pt)
|
|
||||||
sc.verifyUpdateDashboardPermissionsShouldNotBeAllowed(pt)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *scenarioContext) dashboardPermissionScenario(pt permissionType, permission dashboards.PermissionType, flag permissionFlags) {
|
|
||||||
_, callerFile, callerLine, _ := runtime.Caller(1)
|
|
||||||
sc.callerFile = callerFile
|
|
||||||
sc.callerLine = callerLine
|
|
||||||
var existingPermissions []*dashboards.DashboardACLInfoDTO
|
|
||||||
|
|
||||||
switch pt {
|
|
||||||
case USER:
|
|
||||||
existingPermissions = []*dashboards.DashboardACLInfoDTO{{OrgID: orgID, DashboardID: dashboardID, UserID: userID, Permission: permission}}
|
|
||||||
case TEAM:
|
|
||||||
existingPermissions = []*dashboards.DashboardACLInfoDTO{{OrgID: orgID, DashboardID: dashboardID, TeamID: teamID, Permission: permission}}
|
|
||||||
case EDITOR:
|
|
||||||
existingPermissions = []*dashboards.DashboardACLInfoDTO{{OrgID: orgID, DashboardID: dashboardID, Role: &editorRole, Permission: permission}}
|
|
||||||
case VIEWER:
|
|
||||||
existingPermissions = []*dashboards.DashboardACLInfoDTO{{OrgID: orgID, DashboardID: dashboardID, Role: &viewerRole, Permission: permission}}
|
|
||||||
}
|
|
||||||
|
|
||||||
permissionScenario(fmt.Sprintf("and %s has permission to %s dashboard", pt.String(), permission.String()),
|
|
||||||
dashboardID, sc, existingPermissions, func(sc *scenarioContext) {
|
|
||||||
sc.expectedFlags = flag
|
|
||||||
sc.verifyExpectedPermissionsFlags()
|
|
||||||
sc.verifyDuplicatePermissionsShouldNotBeAllowed()
|
|
||||||
sc.verifyUpdateDashboardPermissionsShouldBeAllowed(pt)
|
|
||||||
sc.verifyUpdateDashboardPermissionsShouldNotBeAllowed(pt)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *scenarioContext) parentFolderPermissionScenario(pt permissionType, permission dashboards.PermissionType, flag permissionFlags) {
|
|
||||||
_, callerFile, callerLine, _ := runtime.Caller(1)
|
|
||||||
sc.callerFile = callerFile
|
|
||||||
sc.callerLine = callerLine
|
|
||||||
var folderPermissionList []*dashboards.DashboardACLInfoDTO
|
|
||||||
|
|
||||||
switch pt {
|
|
||||||
case USER:
|
|
||||||
folderPermissionList = []*dashboards.DashboardACLInfoDTO{{OrgID: orgID, DashboardID: parentFolderID,
|
|
||||||
UserID: userID, Permission: permission, Inherited: true}}
|
|
||||||
case TEAM:
|
|
||||||
folderPermissionList = []*dashboards.DashboardACLInfoDTO{{OrgID: orgID, DashboardID: parentFolderID, TeamID: teamID,
|
|
||||||
Permission: permission, Inherited: true}}
|
|
||||||
case EDITOR:
|
|
||||||
folderPermissionList = []*dashboards.DashboardACLInfoDTO{{OrgID: orgID, DashboardID: parentFolderID,
|
|
||||||
Role: &editorRole, Permission: permission, Inherited: true}}
|
|
||||||
case VIEWER:
|
|
||||||
folderPermissionList = []*dashboards.DashboardACLInfoDTO{{OrgID: orgID, DashboardID: parentFolderID,
|
|
||||||
Role: &viewerRole, Permission: permission, Inherited: true}}
|
|
||||||
}
|
|
||||||
|
|
||||||
permissionScenario(fmt.Sprintf("and parent folder has %s with permission to %s", pt.String(), permission.String()),
|
|
||||||
childDashboardID, sc, folderPermissionList, func(sc *scenarioContext) {
|
|
||||||
sc.expectedFlags = flag
|
|
||||||
sc.verifyExpectedPermissionsFlags()
|
|
||||||
sc.verifyDuplicatePermissionsShouldNotBeAllowed()
|
|
||||||
sc.verifyUpdateChildDashboardPermissionsShouldBeAllowed(pt, permission)
|
|
||||||
sc.verifyUpdateChildDashboardPermissionsShouldNotBeAllowed(pt, permission)
|
|
||||||
sc.verifyUpdateChildDashboardPermissionsWithOverrideShouldBeAllowed(pt, permission)
|
|
||||||
sc.verifyUpdateChildDashboardPermissionsWithOverrideShouldNotBeAllowed(pt, permission)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *scenarioContext) verifyExpectedPermissionsFlags() {
|
|
||||||
tc := fmt.Sprintf("should have permissions to %s", sc.expectedFlags.String())
|
|
||||||
sc.t.Run(tc, func(t *testing.T) {
|
|
||||||
canAdmin, err := sc.g.CanAdmin()
|
|
||||||
require.NoError(t, err)
|
|
||||||
canEdit, err := sc.g.CanEdit()
|
|
||||||
require.NoError(t, err)
|
|
||||||
canSave, err := sc.g.CanSave()
|
|
||||||
require.NoError(t, err)
|
|
||||||
canView, err := sc.g.CanView()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var actualFlag permissionFlags
|
|
||||||
|
|
||||||
if canAdmin {
|
|
||||||
actualFlag |= CAN_ADMIN
|
|
||||||
}
|
|
||||||
|
|
||||||
if canEdit {
|
|
||||||
actualFlag |= CAN_EDIT
|
|
||||||
}
|
|
||||||
|
|
||||||
if canSave {
|
|
||||||
actualFlag |= CAN_SAVE
|
|
||||||
}
|
|
||||||
|
|
||||||
if canView {
|
|
||||||
actualFlag |= CAN_VIEW
|
|
||||||
}
|
|
||||||
|
|
||||||
if actualFlag.noAccess() {
|
|
||||||
actualFlag = NO_ACCESS
|
|
||||||
}
|
|
||||||
|
|
||||||
if actualFlag&sc.expectedFlags != actualFlag {
|
|
||||||
sc.reportFailure(tc, sc.expectedFlags.String(), actualFlag.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
sc.reportSuccess()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *scenarioContext) verifyDuplicatePermissionsShouldNotBeAllowed() {
|
|
||||||
if !sc.expectedFlags.canAdmin() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tc := "When updating dashboard permissions with duplicate permission for user should not be allowed"
|
|
||||||
sc.t.Run(tc, func(t *testing.T) {
|
|
||||||
p := []*dashboards.DashboardACL{
|
|
||||||
newDefaultUserPermission(dashboardID, dashboards.PERMISSION_VIEW),
|
|
||||||
newDefaultUserPermission(dashboardID, dashboards.PERMISSION_ADMIN),
|
|
||||||
}
|
|
||||||
sc.updatePermissions = p
|
|
||||||
_, err := sc.g.CheckPermissionBeforeUpdate(dashboards.PERMISSION_ADMIN, p)
|
|
||||||
|
|
||||||
if !errors.Is(err, ErrGuardianPermissionExists) {
|
|
||||||
sc.reportFailure(tc, ErrGuardianPermissionExists, err)
|
|
||||||
}
|
|
||||||
sc.reportSuccess()
|
|
||||||
})
|
|
||||||
|
|
||||||
tc = "When updating dashboard permissions with duplicate permission for team should not be allowed"
|
|
||||||
sc.t.Run(tc, func(t *testing.T) {
|
|
||||||
p := []*dashboards.DashboardACL{
|
|
||||||
newDefaultTeamPermission(dashboardID, dashboards.PERMISSION_VIEW),
|
|
||||||
newDefaultTeamPermission(dashboardID, dashboards.PERMISSION_ADMIN),
|
|
||||||
}
|
|
||||||
sc.updatePermissions = p
|
|
||||||
_, err := sc.g.CheckPermissionBeforeUpdate(dashboards.PERMISSION_ADMIN, p)
|
|
||||||
if !errors.Is(err, ErrGuardianPermissionExists) {
|
|
||||||
sc.reportFailure(tc, ErrGuardianPermissionExists, err)
|
|
||||||
}
|
|
||||||
sc.reportSuccess()
|
|
||||||
})
|
|
||||||
|
|
||||||
tc = "When updating dashboard permissions with duplicate permission for editor role should not be allowed"
|
|
||||||
sc.t.Run(tc, func(t *testing.T) {
|
|
||||||
p := []*dashboards.DashboardACL{
|
|
||||||
newEditorRolePermission(dashboardID, dashboards.PERMISSION_VIEW),
|
|
||||||
newEditorRolePermission(dashboardID, dashboards.PERMISSION_ADMIN),
|
|
||||||
}
|
|
||||||
sc.updatePermissions = p
|
|
||||||
_, err := sc.g.CheckPermissionBeforeUpdate(dashboards.PERMISSION_ADMIN, p)
|
|
||||||
|
|
||||||
if !errors.Is(err, ErrGuardianPermissionExists) {
|
|
||||||
sc.reportFailure(tc, ErrGuardianPermissionExists, err)
|
|
||||||
}
|
|
||||||
sc.reportSuccess()
|
|
||||||
})
|
|
||||||
|
|
||||||
tc = "When updating dashboard permissions with duplicate permission for viewer role should not be allowed"
|
|
||||||
sc.t.Run(tc, func(t *testing.T) {
|
|
||||||
p := []*dashboards.DashboardACL{
|
|
||||||
newViewerRolePermission(dashboardID, dashboards.PERMISSION_VIEW),
|
|
||||||
newViewerRolePermission(dashboardID, dashboards.PERMISSION_ADMIN),
|
|
||||||
}
|
|
||||||
sc.updatePermissions = p
|
|
||||||
_, err := sc.g.CheckPermissionBeforeUpdate(dashboards.PERMISSION_ADMIN, p)
|
|
||||||
if !errors.Is(err, ErrGuardianPermissionExists) {
|
|
||||||
sc.reportFailure(tc, ErrGuardianPermissionExists, err)
|
|
||||||
}
|
|
||||||
sc.reportSuccess()
|
|
||||||
})
|
|
||||||
|
|
||||||
tc = "When updating dashboard permissions with duplicate permission for admin role should not be allowed"
|
|
||||||
sc.t.Run(tc, func(t *testing.T) {
|
|
||||||
p := []*dashboards.DashboardACL{
|
|
||||||
newAdminRolePermission(dashboardID, dashboards.PERMISSION_ADMIN),
|
|
||||||
}
|
|
||||||
sc.updatePermissions = p
|
|
||||||
_, err := sc.g.CheckPermissionBeforeUpdate(dashboards.PERMISSION_ADMIN, p)
|
|
||||||
if !errors.Is(err, ErrGuardianPermissionExists) {
|
|
||||||
sc.reportFailure(tc, ErrGuardianPermissionExists, err)
|
|
||||||
}
|
|
||||||
sc.reportSuccess()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *scenarioContext) verifyUpdateDashboardPermissionsShouldBeAllowed(pt permissionType) {
|
|
||||||
if !sc.expectedFlags.canAdmin() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, p := range []dashboards.PermissionType{dashboards.PERMISSION_ADMIN, dashboards.PERMISSION_EDIT, dashboards.PERMISSION_VIEW} {
|
|
||||||
tc := fmt.Sprintf("When updating dashboard permissions with %s permissions should be allowed", p.String())
|
|
||||||
sc.t.Run(tc, func(t *testing.T) {
|
|
||||||
permissionList := []*dashboards.DashboardACL{}
|
|
||||||
switch pt {
|
|
||||||
case USER:
|
|
||||||
permissionList = []*dashboards.DashboardACL{
|
|
||||||
newEditorRolePermission(dashboardID, p),
|
|
||||||
newViewerRolePermission(dashboardID, p),
|
|
||||||
newCustomUserPermission(dashboardID, otherUserID, p),
|
|
||||||
newDefaultTeamPermission(dashboardID, p),
|
|
||||||
}
|
|
||||||
case TEAM:
|
|
||||||
permissionList = []*dashboards.DashboardACL{
|
|
||||||
newEditorRolePermission(dashboardID, p),
|
|
||||||
newViewerRolePermission(dashboardID, p),
|
|
||||||
newDefaultUserPermission(dashboardID, p),
|
|
||||||
newCustomTeamPermission(dashboardID, otherTeamID, p),
|
|
||||||
}
|
|
||||||
case EDITOR, VIEWER:
|
|
||||||
permissionList = []*dashboards.DashboardACL{
|
|
||||||
newEditorRolePermission(dashboardID, p),
|
|
||||||
newViewerRolePermission(dashboardID, p),
|
|
||||||
newDefaultUserPermission(dashboardID, p),
|
|
||||||
newDefaultTeamPermission(dashboardID, p),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sc.updatePermissions = permissionList
|
|
||||||
ok, err := sc.g.CheckPermissionBeforeUpdate(dashboards.PERMISSION_ADMIN, permissionList)
|
|
||||||
if err != nil {
|
|
||||||
sc.reportFailure(tc, nil, err)
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
sc.reportFailure(tc, false, true)
|
|
||||||
}
|
|
||||||
sc.reportSuccess()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *scenarioContext) verifyUpdateDashboardPermissionsShouldNotBeAllowed(pt permissionType) {
|
|
||||||
if sc.expectedFlags.canAdmin() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, p := range []dashboards.PermissionType{dashboards.PERMISSION_ADMIN, dashboards.PERMISSION_EDIT, dashboards.PERMISSION_VIEW} {
|
|
||||||
tc := fmt.Sprintf("When updating dashboard permissions with %s permissions should NOT be allowed", p.String())
|
|
||||||
sc.t.Run(tc, func(t *testing.T) {
|
|
||||||
permissionList := []*dashboards.DashboardACL{
|
|
||||||
newEditorRolePermission(dashboardID, p),
|
|
||||||
newViewerRolePermission(dashboardID, p),
|
|
||||||
}
|
|
||||||
switch pt {
|
|
||||||
case USER:
|
|
||||||
permissionList = append(permissionList, []*dashboards.DashboardACL{
|
|
||||||
newCustomUserPermission(dashboardID, otherUserID, p),
|
|
||||||
newDefaultTeamPermission(dashboardID, p),
|
|
||||||
}...)
|
|
||||||
case TEAM:
|
|
||||||
permissionList = append(permissionList, []*dashboards.DashboardACL{
|
|
||||||
newDefaultUserPermission(dashboardID, p),
|
|
||||||
newCustomTeamPermission(dashboardID, otherTeamID, p),
|
|
||||||
}...)
|
|
||||||
default:
|
|
||||||
// TODO: Handle other cases?
|
|
||||||
}
|
|
||||||
|
|
||||||
sc.updatePermissions = permissionList
|
|
||||||
ok, err := sc.g.CheckPermissionBeforeUpdate(dashboards.PERMISSION_ADMIN, permissionList)
|
|
||||||
if err != nil {
|
|
||||||
sc.reportFailure(tc, nil, err)
|
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
sc.reportFailure(tc, true, false)
|
|
||||||
}
|
|
||||||
sc.reportSuccess()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *scenarioContext) verifyUpdateChildDashboardPermissionsShouldBeAllowed(pt permissionType, parentFolderPermission dashboards.PermissionType) {
|
|
||||||
if !sc.expectedFlags.canAdmin() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, p := range []dashboards.PermissionType{dashboards.PERMISSION_ADMIN, dashboards.PERMISSION_EDIT, dashboards.PERMISSION_VIEW} {
|
|
||||||
tc := fmt.Sprintf("When updating child dashboard permissions with %s permissions should be allowed", p.String())
|
|
||||||
sc.t.Run(tc, func(t *testing.T) {
|
|
||||||
permissionList := []*dashboards.DashboardACL{}
|
|
||||||
switch pt {
|
|
||||||
case USER:
|
|
||||||
permissionList = []*dashboards.DashboardACL{
|
|
||||||
newEditorRolePermission(childDashboardID, p),
|
|
||||||
newViewerRolePermission(childDashboardID, p),
|
|
||||||
newCustomUserPermission(childDashboardID, otherUserID, p),
|
|
||||||
newDefaultTeamPermission(childDashboardID, p),
|
|
||||||
}
|
|
||||||
case TEAM:
|
|
||||||
permissionList = []*dashboards.DashboardACL{
|
|
||||||
newEditorRolePermission(childDashboardID, p),
|
|
||||||
newViewerRolePermission(childDashboardID, p),
|
|
||||||
newDefaultUserPermission(childDashboardID, p),
|
|
||||||
newCustomTeamPermission(childDashboardID, otherTeamID, p),
|
|
||||||
}
|
|
||||||
case EDITOR:
|
|
||||||
permissionList = []*dashboards.DashboardACL{
|
|
||||||
newViewerRolePermission(childDashboardID, p),
|
|
||||||
newDefaultUserPermission(childDashboardID, p),
|
|
||||||
newDefaultTeamPermission(childDashboardID, p),
|
|
||||||
}
|
|
||||||
|
|
||||||
// permission to update is higher than parent folder permission
|
|
||||||
if p > parentFolderPermission {
|
|
||||||
permissionList = append(permissionList, newEditorRolePermission(childDashboardID, p))
|
|
||||||
}
|
|
||||||
case VIEWER:
|
|
||||||
permissionList = []*dashboards.DashboardACL{
|
|
||||||
newEditorRolePermission(childDashboardID, p),
|
|
||||||
newDefaultUserPermission(childDashboardID, p),
|
|
||||||
newDefaultTeamPermission(childDashboardID, p),
|
|
||||||
}
|
|
||||||
|
|
||||||
// permission to update is higher than parent folder permission
|
|
||||||
if p > parentFolderPermission {
|
|
||||||
permissionList = append(permissionList, newViewerRolePermission(childDashboardID, p))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sc.updatePermissions = permissionList
|
|
||||||
ok, err := sc.g.CheckPermissionBeforeUpdate(dashboards.PERMISSION_ADMIN, permissionList)
|
|
||||||
if err != nil {
|
|
||||||
sc.reportFailure(tc, nil, err)
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
sc.reportFailure(tc, false, true)
|
|
||||||
}
|
|
||||||
sc.reportSuccess()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *scenarioContext) verifyUpdateChildDashboardPermissionsShouldNotBeAllowed(pt permissionType, parentFolderPermission dashboards.PermissionType) {
|
|
||||||
if sc.expectedFlags.canAdmin() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, p := range []dashboards.PermissionType{dashboards.PERMISSION_ADMIN, dashboards.PERMISSION_EDIT, dashboards.PERMISSION_VIEW} {
|
|
||||||
tc := fmt.Sprintf("When updating child dashboard permissions with %s permissions should NOT be allowed", p.String())
|
|
||||||
sc.t.Run(tc, func(t *testing.T) {
|
|
||||||
permissionList := []*dashboards.DashboardACL{}
|
|
||||||
switch pt {
|
|
||||||
case USER:
|
|
||||||
permissionList = []*dashboards.DashboardACL{
|
|
||||||
newEditorRolePermission(childDashboardID, p),
|
|
||||||
newViewerRolePermission(childDashboardID, p),
|
|
||||||
newCustomUserPermission(childDashboardID, otherUserID, p),
|
|
||||||
newDefaultTeamPermission(childDashboardID, p),
|
|
||||||
}
|
|
||||||
case TEAM:
|
|
||||||
permissionList = []*dashboards.DashboardACL{
|
|
||||||
newEditorRolePermission(childDashboardID, p),
|
|
||||||
newViewerRolePermission(childDashboardID, p),
|
|
||||||
newDefaultUserPermission(childDashboardID, p),
|
|
||||||
newCustomTeamPermission(childDashboardID, otherTeamID, p),
|
|
||||||
}
|
|
||||||
case EDITOR:
|
|
||||||
permissionList = []*dashboards.DashboardACL{
|
|
||||||
newViewerRolePermission(childDashboardID, p),
|
|
||||||
newDefaultUserPermission(childDashboardID, p),
|
|
||||||
newDefaultTeamPermission(childDashboardID, p),
|
|
||||||
}
|
|
||||||
|
|
||||||
// permission to update is higher than parent folder permission
|
|
||||||
if p > parentFolderPermission {
|
|
||||||
permissionList = append(permissionList, newEditorRolePermission(childDashboardID, p))
|
|
||||||
}
|
|
||||||
case VIEWER:
|
|
||||||
permissionList = []*dashboards.DashboardACL{
|
|
||||||
newEditorRolePermission(childDashboardID, p),
|
|
||||||
newDefaultUserPermission(childDashboardID, p),
|
|
||||||
newDefaultTeamPermission(childDashboardID, p),
|
|
||||||
}
|
|
||||||
|
|
||||||
// permission to update is higher than parent folder permission
|
|
||||||
if p > parentFolderPermission {
|
|
||||||
permissionList = append(permissionList, newViewerRolePermission(childDashboardID, p))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sc.updatePermissions = permissionList
|
|
||||||
ok, err := sc.g.CheckPermissionBeforeUpdate(dashboards.PERMISSION_ADMIN, permissionList)
|
|
||||||
if err != nil {
|
|
||||||
sc.reportFailure(tc, nil, err)
|
|
||||||
}
|
|
||||||
if ok {
|
|
||||||
sc.reportFailure(tc, true, false)
|
|
||||||
}
|
|
||||||
sc.reportSuccess()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *scenarioContext) verifyUpdateChildDashboardPermissionsWithOverrideShouldBeAllowed(pt permissionType, parentFolderPermission dashboards.PermissionType) {
|
|
||||||
if !sc.expectedFlags.canAdmin() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, p := range []dashboards.PermissionType{dashboards.PERMISSION_ADMIN, dashboards.PERMISSION_EDIT, dashboards.PERMISSION_VIEW} {
|
|
||||||
// permission to update is higher than parent folder permission
|
|
||||||
if p > parentFolderPermission {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
tc := fmt.Sprintf("When updating child dashboard permissions overriding parent %s permission with %s permission should NOT be allowed", pt.String(), p.String())
|
|
||||||
sc.t.Run(tc, func(t *testing.T) {
|
|
||||||
permissionList := []*dashboards.DashboardACL{}
|
|
||||||
switch pt {
|
|
||||||
case USER:
|
|
||||||
permissionList = []*dashboards.DashboardACL{
|
|
||||||
newDefaultUserPermission(childDashboardID, p),
|
|
||||||
}
|
|
||||||
case TEAM:
|
|
||||||
permissionList = []*dashboards.DashboardACL{
|
|
||||||
newDefaultTeamPermission(childDashboardID, p),
|
|
||||||
}
|
|
||||||
case EDITOR:
|
|
||||||
permissionList = []*dashboards.DashboardACL{
|
|
||||||
newEditorRolePermission(childDashboardID, p),
|
|
||||||
}
|
|
||||||
case VIEWER:
|
|
||||||
permissionList = []*dashboards.DashboardACL{
|
|
||||||
newViewerRolePermission(childDashboardID, p),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sc.updatePermissions = permissionList
|
|
||||||
_, err := sc.g.CheckPermissionBeforeUpdate(dashboards.PERMISSION_ADMIN, permissionList)
|
|
||||||
if !errors.Is(err, ErrGuardianOverride) {
|
|
||||||
sc.reportFailure(tc, ErrGuardianOverride, err)
|
|
||||||
}
|
|
||||||
sc.reportSuccess()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *scenarioContext) verifyUpdateChildDashboardPermissionsWithOverrideShouldNotBeAllowed(pt permissionType, parentFolderPermission dashboards.PermissionType) {
|
|
||||||
if !sc.expectedFlags.canAdmin() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, p := range []dashboards.PermissionType{dashboards.PERMISSION_ADMIN, dashboards.PERMISSION_EDIT, dashboards.PERMISSION_VIEW} {
|
|
||||||
// permission to update is lower than or equal to parent folder permission
|
|
||||||
if p <= parentFolderPermission {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
tc := fmt.Sprintf(
|
|
||||||
"When updating child dashboard permissions overriding parent %s permission with %s permission should be allowed",
|
|
||||||
pt.String(), p.String(),
|
|
||||||
)
|
|
||||||
sc.t.Run(tc, func(t *testing.T) {
|
|
||||||
permissionList := []*dashboards.DashboardACL{}
|
|
||||||
switch pt {
|
|
||||||
case USER:
|
|
||||||
permissionList = []*dashboards.DashboardACL{
|
|
||||||
newDefaultUserPermission(childDashboardID, p),
|
|
||||||
}
|
|
||||||
case TEAM:
|
|
||||||
permissionList = []*dashboards.DashboardACL{
|
|
||||||
newDefaultTeamPermission(childDashboardID, p),
|
|
||||||
}
|
|
||||||
case EDITOR:
|
|
||||||
permissionList = []*dashboards.DashboardACL{
|
|
||||||
newEditorRolePermission(childDashboardID, p),
|
|
||||||
}
|
|
||||||
case VIEWER:
|
|
||||||
permissionList = []*dashboards.DashboardACL{
|
|
||||||
newViewerRolePermission(childDashboardID, p),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := sc.g.CheckPermissionBeforeUpdate(dashboards.PERMISSION_ADMIN, permissionList)
|
|
||||||
if err != nil {
|
|
||||||
sc.reportFailure(tc, nil, err)
|
|
||||||
}
|
|
||||||
sc.updatePermissions = permissionList
|
|
||||||
ok, err := sc.g.CheckPermissionBeforeUpdate(dashboards.PERMISSION_ADMIN, permissionList)
|
|
||||||
if err != nil {
|
|
||||||
sc.reportFailure(tc, nil, err)
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
sc.reportFailure(tc, false, true)
|
|
||||||
}
|
|
||||||
sc.reportSuccess()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGuardianGetHiddenACL(t *testing.T) {
|
|
||||||
t.Run("Get hidden ACL tests", func(t *testing.T) {
|
|
||||||
store := dbtest.NewFakeDB()
|
|
||||||
dashSvc := dashboards.NewFakeDashboardService(t)
|
|
||||||
qResult := []*dashboards.DashboardACLInfoDTO{
|
|
||||||
{Inherited: false, UserID: 1, UserLogin: "user1", Permission: dashboards.PERMISSION_EDIT},
|
|
||||||
{Inherited: false, UserID: 2, UserLogin: "user2", Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
{Inherited: true, UserID: 3, UserLogin: "user3", Permission: dashboards.PERMISSION_VIEW},
|
|
||||||
}
|
|
||||||
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardACLInfoListQuery")).Return(qResult, nil)
|
|
||||||
var qResultDash *dashboards.Dashboard
|
|
||||||
dashSvc.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Run(func(args mock.Arguments) {
|
|
||||||
q := args.Get(1).(*dashboards.GetDashboardQuery)
|
|
||||||
qResultDash = &dashboards.Dashboard{
|
|
||||||
ID: q.ID,
|
|
||||||
UID: q.UID,
|
|
||||||
OrgID: q.OrgID,
|
|
||||||
}
|
|
||||||
}).Return(qResultDash, nil)
|
|
||||||
|
|
||||||
cfg := setting.NewCfg()
|
|
||||||
cfg.HiddenUsers = map[string]struct{}{"user2": {}}
|
|
||||||
|
|
||||||
t.Run("Should get hidden acl", func(t *testing.T) {
|
|
||||||
user := &user.SignedInUser{
|
|
||||||
OrgID: orgID,
|
|
||||||
UserID: 1,
|
|
||||||
Login: "user1",
|
|
||||||
}
|
|
||||||
g, err := newDashboardGuardian(context.Background(), cfg, dashboardID, orgID, user, store, dashSvc, &teamtest.FakeService{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
hiddenACL, err := g.GetHiddenACL(cfg)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
require.Equal(t, len(hiddenACL), 1)
|
|
||||||
require.Equal(t, hiddenACL[0].UserID, int64(2))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Grafana admin should not get hidden acl", func(t *testing.T) {
|
|
||||||
user := &user.SignedInUser{
|
|
||||||
OrgID: orgID,
|
|
||||||
UserID: 1,
|
|
||||||
Login: "user1",
|
|
||||||
IsGrafanaAdmin: true,
|
|
||||||
}
|
|
||||||
dashSvc := dashboards.NewFakeDashboardService(t)
|
|
||||||
qResult := &dashboards.Dashboard{}
|
|
||||||
dashSvc.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Run(func(args mock.Arguments) {
|
|
||||||
}).Return(qResult, nil)
|
|
||||||
g, err := newDashboardGuardian(context.Background(), cfg, dashboardID, orgID, user, store, dashSvc, &teamtest.FakeService{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
hiddenACL, err := g.GetHiddenACL(cfg)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
require.Equal(t, len(hiddenACL), 0)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGuardianGetACLWithoutDuplicates(t *testing.T) {
|
|
||||||
t.Run("Get hidden ACL tests", func(t *testing.T) {
|
|
||||||
store := dbtest.NewFakeDB()
|
|
||||||
dashSvc := dashboards.NewFakeDashboardService(t)
|
|
||||||
qResult := []*dashboards.DashboardACLInfoDTO{
|
|
||||||
{Inherited: true, UserID: 3, UserLogin: "user3", Permission: dashboards.PERMISSION_EDIT},
|
|
||||||
{Inherited: false, UserID: 3, UserLogin: "user3", Permission: dashboards.PERMISSION_VIEW},
|
|
||||||
{Inherited: false, UserID: 2, UserLogin: "user2", Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
{Inherited: true, UserID: 4, UserLogin: "user4", Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
{Inherited: false, UserID: 4, UserLogin: "user4", Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
{Inherited: false, UserID: 5, UserLogin: "user5", Permission: dashboards.PERMISSION_EDIT},
|
|
||||||
{Inherited: true, UserID: 6, UserLogin: "user6", Permission: dashboards.PERMISSION_VIEW},
|
|
||||||
{Inherited: false, UserID: 6, UserLogin: "user6", Permission: dashboards.PERMISSION_EDIT},
|
|
||||||
}
|
|
||||||
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardACLInfoListQuery")).Return(qResult, nil)
|
|
||||||
qResultDash := &dashboards.Dashboard{}
|
|
||||||
dashSvc.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Run(func(args mock.Arguments) {
|
|
||||||
q := args.Get(1).(*dashboards.GetDashboardQuery)
|
|
||||||
qResultDash = &dashboards.Dashboard{
|
|
||||||
ID: q.ID,
|
|
||||||
UID: q.UID,
|
|
||||||
OrgID: q.OrgID,
|
|
||||||
}
|
|
||||||
}).Return(qResultDash, nil)
|
|
||||||
|
|
||||||
t.Run("Should get acl without duplicates", func(t *testing.T) {
|
|
||||||
user := &user.SignedInUser{
|
|
||||||
OrgID: orgID,
|
|
||||||
UserID: 1,
|
|
||||||
Login: "user1",
|
|
||||||
}
|
|
||||||
g, err := newDashboardGuardian(context.Background(), setting.NewCfg(), dashboardID, orgID, user, store, dashSvc, &teamtest.FakeService{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
acl, err := g.GetACLWithoutDuplicates()
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, acl)
|
|
||||||
require.Len(t, acl, 6)
|
|
||||||
require.ElementsMatch(t, []*dashboards.DashboardACLInfoDTO{
|
|
||||||
{Inherited: true, UserID: 3, UserLogin: "user3", Permission: dashboards.PERMISSION_EDIT},
|
|
||||||
{Inherited: true, UserID: 4, UserLogin: "user4", Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
{Inherited: true, UserID: 6, UserLogin: "user6", Permission: dashboards.PERMISSION_VIEW},
|
|
||||||
{Inherited: false, UserID: 2, UserLogin: "user2", Permission: dashboards.PERMISSION_ADMIN},
|
|
||||||
{Inherited: false, UserID: 5, UserLogin: "user5", Permission: dashboards.PERMISSION_EDIT},
|
|
||||||
{Inherited: false, UserID: 6, UserLogin: "user6", Permission: dashboards.PERMISSION_EDIT},
|
|
||||||
}, acl)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,303 +0,0 @@
|
|||||||
package guardian
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/infra/db/dbtest"
|
|
||||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
|
||||||
"github.com/grafana/grafana/pkg/services/org"
|
|
||||||
"github.com/grafana/grafana/pkg/services/team"
|
|
||||||
"github.com/grafana/grafana/pkg/services/team/teamtest"
|
|
||||||
"github.com/grafana/grafana/pkg/services/user"
|
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
|
||||||
)
|
|
||||||
|
|
||||||
type scenarioContext struct {
|
|
||||||
t *testing.T
|
|
||||||
orgRoleScenario string
|
|
||||||
permissionScenario string
|
|
||||||
g DashboardGuardian
|
|
||||||
givenUser *user.SignedInUser
|
|
||||||
givenDashboardID int64
|
|
||||||
givenPermissions []*dashboards.DashboardACLInfoDTO
|
|
||||||
givenTeams []*team.TeamDTO
|
|
||||||
updatePermissions []*dashboards.DashboardACL
|
|
||||||
expectedFlags permissionFlags
|
|
||||||
callerFile string
|
|
||||||
callerLine int
|
|
||||||
}
|
|
||||||
|
|
||||||
type scenarioFunc func(c *scenarioContext)
|
|
||||||
|
|
||||||
func orgRoleScenario(desc string, t *testing.T, role org.RoleType, fn scenarioFunc) {
|
|
||||||
t.Run(desc, func(t *testing.T) {
|
|
||||||
user := &user.SignedInUser{
|
|
||||||
UserID: userID,
|
|
||||||
OrgID: orgID,
|
|
||||||
OrgRole: role,
|
|
||||||
}
|
|
||||||
store := dbtest.NewFakeDB()
|
|
||||||
|
|
||||||
fakeDashboardService := dashboards.NewFakeDashboardService(t)
|
|
||||||
var qResult *dashboards.Dashboard
|
|
||||||
fakeDashboardService.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Run(func(args mock.Arguments) {
|
|
||||||
q := args.Get(1).(*dashboards.GetDashboardQuery)
|
|
||||||
qResult = &dashboards.Dashboard{
|
|
||||||
ID: q.ID,
|
|
||||||
UID: q.UID,
|
|
||||||
}
|
|
||||||
}).Return(qResult, nil)
|
|
||||||
guard, err := newDashboardGuardian(context.Background(), setting.NewCfg(), dashboardID, orgID, user, store, fakeDashboardService, &teamtest.FakeService{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
sc := &scenarioContext{
|
|
||||||
t: t,
|
|
||||||
orgRoleScenario: desc,
|
|
||||||
givenUser: user,
|
|
||||||
givenDashboardID: dashboardID,
|
|
||||||
g: guard,
|
|
||||||
}
|
|
||||||
fn(sc)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func apiKeyScenario(desc string, t *testing.T, role org.RoleType, fn scenarioFunc) {
|
|
||||||
t.Run(desc, func(t *testing.T) {
|
|
||||||
user := &user.SignedInUser{
|
|
||||||
UserID: 0,
|
|
||||||
OrgID: orgID,
|
|
||||||
OrgRole: role,
|
|
||||||
ApiKeyID: 10,
|
|
||||||
}
|
|
||||||
store := dbtest.NewFakeDB()
|
|
||||||
dashSvc := dashboards.NewFakeDashboardService(t)
|
|
||||||
var qResult *dashboards.Dashboard
|
|
||||||
dashSvc.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Run(func(args mock.Arguments) {
|
|
||||||
q := args.Get(1).(*dashboards.GetDashboardQuery)
|
|
||||||
qResult = &dashboards.Dashboard{
|
|
||||||
ID: q.ID,
|
|
||||||
UID: q.UID,
|
|
||||||
}
|
|
||||||
}).Return(qResult, nil)
|
|
||||||
guard, err := newDashboardGuardian(context.Background(), setting.NewCfg(), dashboardID, orgID, user, store, dashSvc, &teamtest.FakeService{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
sc := &scenarioContext{
|
|
||||||
t: t,
|
|
||||||
orgRoleScenario: desc,
|
|
||||||
givenUser: user,
|
|
||||||
givenDashboardID: dashboardID,
|
|
||||||
g: guard,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn(sc)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func permissionScenario(desc string, dashboardID int64, sc *scenarioContext,
|
|
||||||
permissions []*dashboards.DashboardACLInfoDTO, fn scenarioFunc) {
|
|
||||||
sc.t.Run(desc, func(t *testing.T) {
|
|
||||||
store := dbtest.NewFakeDB()
|
|
||||||
teams := []*team.TeamDTO{}
|
|
||||||
|
|
||||||
for _, p := range permissions {
|
|
||||||
if p.TeamID > 0 {
|
|
||||||
teams = append(teams, &team.TeamDTO{ID: p.TeamID})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
teamSvc := &teamtest.FakeService{ExpectedTeamsByUser: teams}
|
|
||||||
|
|
||||||
dashSvc := dashboards.NewFakeDashboardService(t)
|
|
||||||
qResult := permissions
|
|
||||||
dashSvc.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardACLInfoListQuery")).Return(qResult, nil)
|
|
||||||
qResultDash := &dashboards.Dashboard{}
|
|
||||||
dashSvc.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Run(func(args mock.Arguments) {
|
|
||||||
q := args.Get(1).(*dashboards.GetDashboardQuery)
|
|
||||||
qResultDash = &dashboards.Dashboard{
|
|
||||||
ID: q.ID,
|
|
||||||
UID: q.UID,
|
|
||||||
OrgID: q.OrgID,
|
|
||||||
}
|
|
||||||
}).Return(qResultDash, nil)
|
|
||||||
|
|
||||||
sc.permissionScenario = desc
|
|
||||||
g, err := newDashboardGuardian(context.Background(), setting.NewCfg(), dashboardID, sc.givenUser.OrgID, sc.givenUser, store, dashSvc, teamSvc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
sc.g = g
|
|
||||||
|
|
||||||
sc.givenDashboardID = dashboardID
|
|
||||||
sc.givenPermissions = permissions
|
|
||||||
sc.givenTeams = teams
|
|
||||||
|
|
||||||
fn(sc)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type permissionType uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
USER permissionType = 1 << iota
|
|
||||||
TEAM
|
|
||||||
EDITOR
|
|
||||||
VIEWER
|
|
||||||
)
|
|
||||||
|
|
||||||
func (p permissionType) String() string {
|
|
||||||
names := map[uint8]string{
|
|
||||||
uint8(USER): "user",
|
|
||||||
uint8(TEAM): "team",
|
|
||||||
uint8(EDITOR): "editor role",
|
|
||||||
uint8(VIEWER): "viewer role",
|
|
||||||
}
|
|
||||||
return names[uint8(p)]
|
|
||||||
}
|
|
||||||
|
|
||||||
type permissionFlags uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
NO_ACCESS permissionFlags = 1 << iota
|
|
||||||
CAN_ADMIN
|
|
||||||
CAN_EDIT
|
|
||||||
CAN_SAVE
|
|
||||||
CAN_VIEW
|
|
||||||
FULL_ACCESS = CAN_ADMIN | CAN_EDIT | CAN_SAVE | CAN_VIEW
|
|
||||||
EDITOR_ACCESS = CAN_EDIT | CAN_SAVE | CAN_VIEW
|
|
||||||
VIEWER_ACCESS = CAN_VIEW
|
|
||||||
)
|
|
||||||
|
|
||||||
func (f permissionFlags) canAdmin() bool {
|
|
||||||
return f&CAN_ADMIN != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f permissionFlags) canEdit() bool {
|
|
||||||
return f&CAN_EDIT != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f permissionFlags) canSave() bool {
|
|
||||||
return f&CAN_SAVE != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f permissionFlags) canView() bool {
|
|
||||||
return f&CAN_VIEW != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f permissionFlags) noAccess() bool {
|
|
||||||
return f&(CAN_ADMIN|CAN_EDIT|CAN_SAVE|CAN_VIEW) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f permissionFlags) String() string {
|
|
||||||
r := []string{}
|
|
||||||
|
|
||||||
if f.canAdmin() {
|
|
||||||
r = append(r, "admin")
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.canEdit() {
|
|
||||||
r = append(r, "edit")
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.canSave() {
|
|
||||||
r = append(r, "save")
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.canView() {
|
|
||||||
r = append(r, "view")
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.noAccess() {
|
|
||||||
r = append(r, "<no access>")
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.Join(r, ", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *scenarioContext) reportSuccess() {
|
|
||||||
assert.True(sc.t, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sc *scenarioContext) reportFailure(desc string, expected interface{}, actual interface{}) {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
buf.WriteString("\n")
|
|
||||||
buf.WriteString(sc.orgRoleScenario)
|
|
||||||
buf.WriteString(" ")
|
|
||||||
buf.WriteString(sc.permissionScenario)
|
|
||||||
buf.WriteString("\n ")
|
|
||||||
buf.WriteString(desc)
|
|
||||||
buf.WriteString("\n")
|
|
||||||
buf.WriteString(fmt.Sprintf("Source test: %s:%d\n", sc.callerFile, sc.callerLine))
|
|
||||||
buf.WriteString(fmt.Sprintf("Expected: %v\n", expected))
|
|
||||||
buf.WriteString(fmt.Sprintf("Actual: %v\n", actual))
|
|
||||||
buf.WriteString("Context:")
|
|
||||||
buf.WriteString(fmt.Sprintf("\n Given user: orgRole=%s, id=%d, orgId=%d", sc.givenUser.OrgRole, sc.givenUser.UserID, sc.givenUser.OrgID))
|
|
||||||
buf.WriteString(fmt.Sprintf("\n Given dashboard id: %d", sc.givenDashboardID))
|
|
||||||
|
|
||||||
for i, p := range sc.givenPermissions {
|
|
||||||
r := "<nil>"
|
|
||||||
if p.Role != nil {
|
|
||||||
r = string(*p.Role)
|
|
||||||
}
|
|
||||||
buf.WriteString(fmt.Sprintf("\n Given permission (%d): dashboardId=%d, userId=%d, teamId=%d, role=%v, permission=%s", i, p.DashboardID, p.UserID, p.TeamID, r, p.Permission.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, t := range sc.givenTeams {
|
|
||||||
buf.WriteString(fmt.Sprintf("\n Given team (%d): id=%d", i, t.ID))
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, p := range sc.updatePermissions {
|
|
||||||
r := "<nil>"
|
|
||||||
if p.Role != nil {
|
|
||||||
r = string(*p.Role)
|
|
||||||
}
|
|
||||||
buf.WriteString(fmt.Sprintf("\n Update permission (%d): dashboardId=%d, userId=%d, teamId=%d, role=%v, permission=%s", i, p.DashboardID, p.UserID, p.TeamID, r, p.Permission.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
sc.t.Fatalf(buf.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCustomUserPermission(dashboardID int64, userID int64, permission dashboards.PermissionType) *dashboards.DashboardACL {
|
|
||||||
return &dashboards.DashboardACL{OrgID: orgID, DashboardID: dashboardID, UserID: userID, Permission: permission}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDefaultUserPermission(dashboardID int64, permission dashboards.PermissionType) *dashboards.DashboardACL {
|
|
||||||
return newCustomUserPermission(dashboardID, userID, permission)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newCustomTeamPermission(dashboardID int64, teamID int64, permission dashboards.PermissionType) *dashboards.DashboardACL {
|
|
||||||
return &dashboards.DashboardACL{OrgID: orgID, DashboardID: dashboardID, TeamID: teamID, Permission: permission}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newDefaultTeamPermission(dashboardID int64, permission dashboards.PermissionType) *dashboards.DashboardACL {
|
|
||||||
return newCustomTeamPermission(dashboardID, teamID, permission)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newAdminRolePermission(dashboardID int64, permission dashboards.PermissionType) *dashboards.DashboardACL {
|
|
||||||
return &dashboards.DashboardACL{OrgID: orgID, DashboardID: dashboardID, Role: &adminRole, Permission: permission}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newEditorRolePermission(dashboardID int64, permission dashboards.PermissionType) *dashboards.DashboardACL {
|
|
||||||
return &dashboards.DashboardACL{OrgID: orgID, DashboardID: dashboardID, Role: &editorRole, Permission: permission}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newViewerRolePermission(dashboardID int64, permission dashboards.PermissionType) *dashboards.DashboardACL {
|
|
||||||
return &dashboards.DashboardACL{OrgID: orgID, DashboardID: dashboardID, Role: &viewerRole, Permission: permission}
|
|
||||||
}
|
|
||||||
|
|
||||||
func toDto(acl *dashboards.DashboardACL) *dashboards.DashboardACLInfoDTO {
|
|
||||||
return &dashboards.DashboardACLInfoDTO{
|
|
||||||
OrgID: acl.OrgID,
|
|
||||||
DashboardID: acl.DashboardID,
|
|
||||||
UserID: acl.UserID,
|
|
||||||
TeamID: acl.TeamID,
|
|
||||||
Role: acl.Role,
|
|
||||||
Permission: acl.Permission,
|
|
||||||
PermissionName: acl.Permission.String(),
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user