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:
+1
-27
@@ -11,13 +11,11 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/api/routing"
|
||||
"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/tracing"
|
||||
"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/contexthandler"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
|
||||
"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/login"
|
||||
"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/model"
|
||||
"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/usertest"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
@@ -296,31 +291,10 @@ var (
|
||||
editorRole = org.RoleEditor
|
||||
)
|
||||
|
||||
type setUpConf struct {
|
||||
aclMockResp []*dashboards.DashboardACLInfoDTO
|
||||
}
|
||||
|
||||
type mockSearchService struct{ ExpectedResult model.HitList }
|
||||
|
||||
func (mss *mockSearchService) SearchHandler(_ context.Context, q *search.Query) (model.HitList, error) {
|
||||
return mss.ExpectedResult, 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 (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
@@ -10,9 +9,10 @@ import (
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -57,16 +57,7 @@ func (hs *HTTPServer) GetDashboardPermissionList(c *contextmodel.ReqContext) res
|
||||
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)
|
||||
}
|
||||
|
||||
acl, err := g.GetACLWithoutDuplicates()
|
||||
acl, err := hs.getDashboardACL(c.Req.Context(), c.SignedInUser, dash)
|
||||
if err != nil {
|
||||
return response.Error(500, "Failed to get dashboard permissions", err)
|
||||
}
|
||||
@@ -147,15 +138,6 @@ func (hs *HTTPServer) UpdateDashboardPermissions(c *contextmodel.ReqContext) res
|
||||
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))
|
||||
for _, item := range apiCmd.Items {
|
||||
items = append(items, &dashboards.DashboardACL{
|
||||
@@ -170,34 +152,102 @@ func (hs *HTTPServer) UpdateDashboardPermissions(c *contextmodel.ReqContext) res
|
||||
})
|
||||
}
|
||||
|
||||
hiddenACL, err := g.GetHiddenACL(hs.Cfg)
|
||||
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()
|
||||
acl, err := hs.getDashboardACL(c.Req.Context(), c.SignedInUser, dash)
|
||||
if err != nil {
|
||||
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.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
|
||||
func (hs *HTTPServer) updateDashboardAccessControl(ctx context.Context, orgID int64, uid string, isFolder bool, items []*dashboards.DashboardACL, old []*dashboards.DashboardACLInfoDTO) error {
|
||||
commands := []accesscontrol.SetResourcePermissionCommand{}
|
||||
|
||||
@@ -2,370 +2,158 @@ package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"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"
|
||||
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||
"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/web/webtest"
|
||||
)
|
||||
|
||||
func TestDashboardPermissionAPIEndpoint(t *testing.T) {
|
||||
t.Run("Dashboard permissions test", func(t *testing.T) {
|
||||
settings := setting.NewCfg()
|
||||
dashboardStore := &dashboards.FakeDashboardStore{}
|
||||
qResult := &dashboards.Dashboard{}
|
||||
dashboardStore.On("GetDashboard", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardQuery")).Return(qResult, nil)
|
||||
defer dashboardStore.AssertExpectations(t)
|
||||
func TestHTTPServer_GetDashboardPermissionList(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) {
|
||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {})
|
||||
|
||||
features := featuremgmt.WithFeatures()
|
||||
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,
|
||||
)
|
||||
res, err := server.Send(webtest.RequestWithSignedInUser(server.NewGetRequest("/api/dashboards/uid/1/permissions"), userWithPermissions(1, nil)))
|
||||
require.NoError(t, err)
|
||||
hs := &HTTPServer{
|
||||
Cfg: settings,
|
||||
SQLStore: mockSQLStore,
|
||||
Features: features,
|
||||
DashboardService: dashboardService,
|
||||
AccessControl: accesscontrolmock.New(),
|
||||
dashboardPermissionsService: dashboardPermissions,
|
||||
}
|
||||
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
||||
require.NoError(t, res.Body.Close())
|
||||
})
|
||||
|
||||
t.Run("Given user has no admin permissions", func(t *testing.T) {
|
||||
origNewGuardian := guardian.New
|
||||
t.Cleanup(func() {
|
||||
guardian.New = origNewGuardian
|
||||
})
|
||||
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanAdminValue: false})
|
||||
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("should be able to list acl with correct permission", func(t *testing.T) {
|
||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
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{},
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("When trying to override inherited permissions with lower precedence", func(t *testing.T) {
|
||||
origNewGuardian := guardian.New
|
||||
t.Cleanup(func() {
|
||||
guardian.New = origNewGuardian
|
||||
})
|
||||
res, err := server.Send(webtest.RequestWithSignedInUser(server.NewGetRequest("/api/dashboards/uid/1/permissions"), userWithPermissions(1, []accesscontrol.Permission{
|
||||
{Action: dashboards.ActionDashboardsPermissionsRead, Scope: "dashboards:uid:1"},
|
||||
})))
|
||||
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
|
||||
CanAdminValue: true,
|
||||
CheckPermissionBeforeUpdateValue: false,
|
||||
CheckPermissionBeforeUpdateError: guardian.ErrGuardianOverride},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||
require.NoError(t, res.Body.Close())
|
||||
})
|
||||
|
||||
cmd := dtos.UpdateDashboardACLCommand{
|
||||
Items: []dtos.DashboardACLUpdateItem{
|
||||
{UserID: 1000, Permission: dashboards.PERMISSION_ADMIN},
|
||||
t.Run("should filter out hidden users from acl", func(t *testing.T) {
|
||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
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) {
|
||||
origNewGuardian := guardian.New
|
||||
settings.HiddenUsers = map[string]struct{}{
|
||||
"hiddenUser": {},
|
||||
testUserLogin: {},
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
guardian.New = origNewGuardian
|
||||
settings.HiddenUsers = make(map[string]struct{})
|
||||
})
|
||||
res, err := server.Send(webtest.RequestWithSignedInUser(server.NewGetRequest("/api/dashboards/uid/1/permissions"), userWithPermissions(1, []accesscontrol.Permission{
|
||||
{Action: dashboards.ActionDashboardsPermissionsRead, Scope: "dashboards:uid:1"},
|
||||
})))
|
||||
|
||||
mockSQLStore := dbtest.NewFakeDB()
|
||||
var resp []*dashboards.DashboardACLInfoDTO
|
||||
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},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||
|
||||
callGetDashboardPermissions(sc, hs)
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
var result []dashboards.DashboardACLInfoDTO
|
||||
require.NoError(t, json.NewDecoder(res.Body).Decode(&result))
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
})
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0].UserLogin, "regular")
|
||||
require.NoError(t, res.Body.Close())
|
||||
})
|
||||
}
|
||||
|
||||
func callGetDashboardPermissions(sc *scenarioContext, hs *HTTPServer) {
|
||||
sc.handlerFunc = hs.GetDashboardPermissionList
|
||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
||||
}
|
||||
func TestHTTPServer_UpdateDashboardPermissions(t *testing.T) {
|
||||
t.Run("should not be able to update acl when user does not have permission to do so", func(t *testing.T) {
|
||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {})
|
||||
|
||||
func callUpdateDashboardPermissions(t *testing.T, sc *scenarioContext) {
|
||||
t.Helper()
|
||||
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
||||
}
|
||||
res, err := server.Send(webtest.RequestWithSignedInUser(server.NewPostRequest("/api/dashboards/uid/1/permissions", nil), userWithPermissions(1, nil)))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
||||
require.NoError(t, res.Body.Close())
|
||||
})
|
||||
|
||||
type updatePermissionContext struct {
|
||||
desc string
|
||||
url string
|
||||
routePattern string
|
||||
cmd dtos.UpdateDashboardACLCommand
|
||||
fn scenarioFunc
|
||||
}
|
||||
t.Run("should be able to update acl with correct permissions", func(t *testing.T) {
|
||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
svc := dashboards.NewFakeDashboardService(t)
|
||||
svc.On("GetDashboard", mock.Anything, mock.Anything).Return(&dashboards.Dashboard{ID: 1, UID: "1"}, nil)
|
||||
|
||||
func updateDashboardPermissionScenario(t *testing.T, ctx updatePermissionContext, hs *HTTPServer) {
|
||||
t.Run(fmt.Sprintf("%s %s", ctx.desc, ctx.url), func(t *testing.T) {
|
||||
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)
|
||||
hs.DashboardService = svc
|
||||
hs.dashboardPermissionsService = &actest.FakePermissionsService{}
|
||||
})
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/apierrors"
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -33,16 +34,7 @@ func (hs *HTTPServer) GetFolderPermissionList(c *contextmodel.ReqContext) respon
|
||||
return apierrors.ToFolderErrorResponse(err)
|
||||
}
|
||||
|
||||
g, err := guardian.New(c.Req.Context(), folder.ID, c.OrgID, c.SignedInUser)
|
||||
if err != nil {
|
||||
return response.Err(err)
|
||||
}
|
||||
|
||||
if canAdmin, err := g.CanAdmin(); err != nil || !canAdmin {
|
||||
return apierrors.ToFolderErrorResponse(dashboards.ErrFolderAccessDenied)
|
||||
}
|
||||
|
||||
acl, err := g.GetACL()
|
||||
acl, err := hs.getFolderACL(c.Req.Context(), c.SignedInUser, folder)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
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))
|
||||
for _, item := range apiCmd.Items {
|
||||
items = append(items, &dashboards.DashboardACL{
|
||||
@@ -125,35 +103,72 @@ func (hs *HTTPServer) UpdateFolderPermissions(c *contextmodel.ReqContext) respon
|
||||
})
|
||||
}
|
||||
|
||||
hiddenACL, err := g.GetHiddenACL(hs.Cfg)
|
||||
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()
|
||||
acl, err := hs.getFolderACL(c.Req.Context(), c.SignedInUser, folder)
|
||||
if err != nil {
|
||||
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.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
|
||||
type GetFolderPermissionListParams struct {
|
||||
// in:path
|
||||
|
||||
+111
-333
@@ -2,373 +2,151 @@ package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"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"
|
||||
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
||||
"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/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/web/webtest"
|
||||
)
|
||||
|
||||
func TestFolderPermissionAPIEndpoint(t *testing.T) {
|
||||
settings := setting.NewCfg()
|
||||
func TestHTTPServer_GetFolderPermissionList(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) {
|
||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {})
|
||||
|
||||
folderService := &foldertest.FakeService{}
|
||||
|
||||
dashboardStore := &dashboards.FakeDashboardStore{}
|
||||
defer dashboardStore.AssertExpectations(t)
|
||||
|
||||
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)
|
||||
res, err := server.Send(webtest.RequestWithSignedInUser(server.NewGetRequest("/api/folders/1/permissions"), userWithPermissions(1, nil)))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
||||
require.NoError(t, res.Body.Close())
|
||||
})
|
||||
|
||||
t.Run("Given user has no admin permissions", func(t *testing.T) {
|
||||
origNewGuardian := guardian.New
|
||||
t.Cleanup(func() {
|
||||
guardian.New = origNewGuardian
|
||||
folderService.ExpectedError = nil
|
||||
t.Run("should be able to list acl with correct permission", 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{
|
||||
ExpectedPermissions: []accesscontrol.ResourcePermission{},
|
||||
}
|
||||
})
|
||||
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanAdminValue: false})
|
||||
folderService.ExpectedError = dashboards.ErrFolderAccessDenied
|
||||
mockSQLStore := dbtest.NewFakeDB()
|
||||
res, err := server.Send(webtest.RequestWithSignedInUser(server.NewGetRequest("/api/folders/1/permissions"), userWithPermissions(1, []accesscontrol.Permission{
|
||||
{Action: dashboards.ActionFoldersPermissionsRead, Scope: "folders:uid:1"},
|
||||
})))
|
||||
|
||||
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, 403, 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, 403, sc.resp.Code)
|
||||
},
|
||||
}, hs)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||
require.NoError(t, res.Body.Close())
|
||||
})
|
||||
|
||||
t.Run("Given user has admin permissions and permissions to update", func(t *testing.T) {
|
||||
origNewGuardian := guardian.New
|
||||
t.Cleanup(func() {
|
||||
guardian.New = origNewGuardian
|
||||
})
|
||||
t.Run("should filter out hidden users from acl", func(t *testing.T) {
|
||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.HiddenUsers = map[string]struct{}{"hidden": {}}
|
||||
hs.Cfg = cfg
|
||||
hs.folderService = &foldertest.FakeService{ExpectedFolder: &folder.Folder{ID: 1, UID: "1"}}
|
||||
|
||||
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},
|
||||
},
|
||||
})
|
||||
|
||||
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},
|
||||
hs.folderPermissionsService = &actest.FakePermissionsService{
|
||||
ExpectedPermissions: []accesscontrol.ResourcePermission{
|
||||
{UserId: 1, UserLogin: "regular", IsManaged: true},
|
||||
{UserId: 2, UserLogin: "hidden", IsManaged: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
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{
|
||||
CanAdminValue: true,
|
||||
CheckPermissionBeforeUpdateValue: false,
|
||||
CheckPermissionBeforeUpdateError: guardian.ErrGuardianOverride},
|
||||
)
|
||||
res, err := server.Send(webtest.RequestWithSignedInUser(server.NewGetRequest("/api/folders/1/permissions"), userWithPermissions(1, []accesscontrol.Permission{
|
||||
{Action: dashboards.ActionFoldersPermissionsRead, Scope: "folders:uid:1"},
|
||||
})))
|
||||
|
||||
folderService.ExpectedFolder = &folder.Folder{ID: 1, UID: "uid", Title: "Folder"}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||
|
||||
cmd := dtos.UpdateDashboardACLCommand{
|
||||
Items: []dtos.DashboardACLUpdateItem{
|
||||
{UserID: 1000, Permission: dashboards.PERMISSION_ADMIN},
|
||||
},
|
||||
}
|
||||
var result []dashboards.DashboardACLInfoDTO
|
||||
require.NoError(t, json.NewDecoder(res.Body).Decode(&result))
|
||||
|
||||
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("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)
|
||||
assert.Len(t, result, 1)
|
||||
assert.Equal(t, result[0].UserLogin, "regular")
|
||||
require.NoError(t, res.Body.Close())
|
||||
})
|
||||
}
|
||||
|
||||
func callGetFolderPermissions(sc *scenarioContext, hs *HTTPServer) {
|
||||
sc.handlerFunc = hs.GetFolderPermissionList
|
||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
||||
}
|
||||
func TestHTTPServer_UpdateFolderPermissions(t *testing.T) {
|
||||
t.Run("should not be able to update acl when user does not have permission to do so", func(t *testing.T) {
|
||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {})
|
||||
|
||||
func callUpdateFolderPermissions(t *testing.T, sc *scenarioContext) {
|
||||
t.Helper()
|
||||
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
||||
}
|
||||
res, err := server.Send(webtest.RequestWithSignedInUser(server.NewPostRequest("/api/folders/1/permissions", nil), userWithPermissions(1, nil)))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusForbidden, res.StatusCode)
|
||||
require.NoError(t, res.Body.Close())
|
||||
})
|
||||
|
||||
func updateFolderPermissionScenario(t *testing.T, ctx updatePermissionContext, hs *HTTPServer) {
|
||||
t.Run(fmt.Sprintf("%s %s", ctx.desc, ctx.url), func(t *testing.T) {
|
||||
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.UpdateFolderPermissions(c)
|
||||
t.Run("should be able to update acl with correct permissions", 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{}
|
||||
})
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/mail"
|
||||
|
||||
"github.com/grafana/grafana/pkg/middleware/cookies"
|
||||
@@ -21,12 +20,6 @@ func (hs *HTTPServer) GetRedirectURL(c *contextmodel.ReqContext) string {
|
||||
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) {
|
||||
if email == "" {
|
||||
return "", nil
|
||||
|
||||
Reference in New Issue
Block a user