mirror of
https://github.com/grafana/grafana.git
synced 2025-02-14 01:23:32 -06:00
RBAC: remove dashboard ACL logic from dash store and service (#78130)
remove dashboard ACL logic from dash store and service
This commit is contained in:
parent
1a53a716e9
commit
dd54931147
@ -227,12 +227,6 @@ func (hs *HTTPServer) AdminDeleteUser(c *contextmodel.ReqContext) response.Respo
|
||||
}
|
||||
return nil
|
||||
})
|
||||
g.Go(func() error {
|
||||
if err := hs.DashboardService.DeleteACLByUser(ctx, cmd.UserID); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
g.Go(func() error {
|
||||
if err := hs.preferenceService.DeleteByUser(ctx, cmd.UserID); err != nil {
|
||||
return err
|
||||
|
@ -17,15 +17,12 @@ type DashboardService interface {
|
||||
DeleteDashboard(ctx context.Context, dashboardId int64, orgId int64) error
|
||||
FindDashboards(ctx context.Context, query *FindPersistedDashboardsQuery) ([]DashboardSearchProjection, error)
|
||||
GetDashboard(ctx context.Context, query *GetDashboardQuery) (*Dashboard, error)
|
||||
GetDashboardACLInfoList(ctx context.Context, query *GetDashboardACLInfoListQuery) ([]*DashboardACLInfoDTO, error)
|
||||
GetDashboards(ctx context.Context, query *GetDashboardsQuery) ([]*Dashboard, error)
|
||||
GetDashboardTags(ctx context.Context, query *GetDashboardTagsQuery) ([]*DashboardTagCloudItem, error)
|
||||
GetDashboardUIDByID(ctx context.Context, query *GetDashboardRefByIDQuery) (*DashboardRef, error)
|
||||
ImportDashboard(ctx context.Context, dto *SaveDashboardDTO) (*Dashboard, error)
|
||||
SaveDashboard(ctx context.Context, dto *SaveDashboardDTO, allowUiUpdate bool) (*Dashboard, error)
|
||||
SearchDashboards(ctx context.Context, query *FindPersistedDashboardsQuery) (model.HitList, error)
|
||||
UpdateDashboardACL(ctx context.Context, uid int64, items []*DashboardACL) error
|
||||
DeleteACLByUser(ctx context.Context, userID int64) error
|
||||
CountInFolder(ctx context.Context, orgID int64, folderUID string, user identity.Requester) (int64, error)
|
||||
}
|
||||
|
||||
@ -56,7 +53,6 @@ type Store interface {
|
||||
DeleteOrphanedProvisionedDashboards(ctx context.Context, cmd *DeleteOrphanedProvisionedDashboardsCommand) error
|
||||
FindDashboards(ctx context.Context, query *FindPersistedDashboardsQuery) ([]DashboardSearchProjection, error)
|
||||
GetDashboard(ctx context.Context, query *GetDashboardQuery) (*Dashboard, error)
|
||||
GetDashboardACLInfoList(ctx context.Context, query *GetDashboardACLInfoListQuery) ([]*DashboardACLInfoDTO, error)
|
||||
GetDashboardUIDByID(ctx context.Context, query *GetDashboardRefByIDQuery) (*DashboardRef, error)
|
||||
GetDashboards(ctx context.Context, query *GetDashboardsQuery) ([]*Dashboard, error)
|
||||
// GetDashboardsByPluginID retrieves dashboards identified by plugin.
|
||||
@ -70,10 +66,8 @@ type Store interface {
|
||||
SaveDashboard(ctx context.Context, cmd SaveDashboardCommand) (*Dashboard, error)
|
||||
SaveProvisionedDashboard(ctx context.Context, cmd SaveDashboardCommand, provisioning *DashboardProvisioning) (*Dashboard, error)
|
||||
UnprovisionDashboard(ctx context.Context, id int64) error
|
||||
UpdateDashboardACL(ctx context.Context, uid int64, items []*DashboardACL) error
|
||||
// ValidateDashboardBeforeSave validates a dashboard before save.
|
||||
ValidateDashboardBeforeSave(ctx context.Context, dashboard *Dashboard, overwrite bool) (bool, error)
|
||||
DeleteACLByUser(context.Context, int64) error
|
||||
|
||||
Count(context.Context, *quota.ScopeParameters) (*quota.Map, error)
|
||||
// CountDashboardsInFolder returns the number of dashboards associated with
|
||||
|
@ -64,20 +64,6 @@ func (_m *FakeDashboardService) CountInFolder(ctx context.Context, orgID int64,
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// DeleteACLByUser provides a mock function with given fields: ctx, userID
|
||||
func (_m *FakeDashboardService) DeleteACLByUser(ctx context.Context, userID int64) error {
|
||||
ret := _m.Called(ctx, userID)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok {
|
||||
r0 = rf(ctx, userID)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// DeleteDashboard provides a mock function with given fields: ctx, dashboardId, orgId
|
||||
func (_m *FakeDashboardService) DeleteDashboard(ctx context.Context, dashboardId int64, orgId int64) error {
|
||||
ret := _m.Called(ctx, dashboardId, orgId)
|
||||
@ -144,32 +130,6 @@ func (_m *FakeDashboardService) GetDashboard(ctx context.Context, query *GetDash
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetDashboardACLInfoList provides a mock function with given fields: ctx, query
|
||||
func (_m *FakeDashboardService) GetDashboardACLInfoList(ctx context.Context, query *GetDashboardACLInfoListQuery) ([]*DashboardACLInfoDTO, error) {
|
||||
ret := _m.Called(ctx, query)
|
||||
|
||||
var r0 []*DashboardACLInfoDTO
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *GetDashboardACLInfoListQuery) ([]*DashboardACLInfoDTO, error)); ok {
|
||||
return rf(ctx, query)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *GetDashboardACLInfoListQuery) []*DashboardACLInfoDTO); ok {
|
||||
r0 = rf(ctx, query)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*DashboardACLInfoDTO)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *GetDashboardACLInfoListQuery) error); ok {
|
||||
r1 = rf(ctx, query)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetDashboardTags provides a mock function with given fields: ctx, query
|
||||
func (_m *FakeDashboardService) GetDashboardTags(ctx context.Context, query *GetDashboardTagsQuery) ([]*DashboardTagCloudItem, error) {
|
||||
ret := _m.Called(ctx, query)
|
||||
@ -326,20 +286,6 @@ func (_m *FakeDashboardService) SearchDashboards(ctx context.Context, query *Fin
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// UpdateDashboardACL provides a mock function with given fields: ctx, uid, items
|
||||
func (_m *FakeDashboardService) UpdateDashboardACL(ctx context.Context, uid int64, items []*DashboardACL) error {
|
||||
ret := _m.Called(ctx, uid, items)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64, []*DashboardACL) error); ok {
|
||||
r0 = rf(ctx, uid, items)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
type mockConstructorTestingTNewFakeDashboardService interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
|
@ -1,103 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
)
|
||||
|
||||
// GetDashboardACLInfoList returns a list of permissions for a dashboard. They can be fetched from three
|
||||
// different places.
|
||||
// 1) Permissions for the dashboard
|
||||
// 2) permissions for its parent folder
|
||||
// 3) if no specific permissions have been set for the dashboard or its parent folder then get the default permissions
|
||||
func (d *dashboardStore) GetDashboardACLInfoList(ctx context.Context, query *dashboards.GetDashboardACLInfoListQuery) ([]*dashboards.DashboardACLInfoDTO, error) {
|
||||
queryResult := make([]*dashboards.DashboardACLInfoDTO, 0)
|
||||
outerErr := d.store.WithDbSession(ctx, func(dbSession *db.Session) error {
|
||||
falseStr := d.store.GetDialect().BooleanStr(false)
|
||||
|
||||
if query.DashboardID == 0 {
|
||||
sql := `SELECT
|
||||
da.id,
|
||||
da.org_id,
|
||||
da.dashboard_id,
|
||||
da.user_id,
|
||||
da.team_id,
|
||||
da.permission,
|
||||
da.role,
|
||||
da.created,
|
||||
da.updated,
|
||||
'' as user_login,
|
||||
'' as user_email,
|
||||
'' as team,
|
||||
'' as title,
|
||||
'' as slug,
|
||||
'' as uid,` +
|
||||
falseStr + ` AS is_folder,` +
|
||||
falseStr + ` AS inherited
|
||||
FROM dashboard_acl as da
|
||||
WHERE da.dashboard_id = -1`
|
||||
return dbSession.SQL(sql).Find(&queryResult)
|
||||
}
|
||||
|
||||
rawSQL := `
|
||||
-- get permissions for the dashboard and its parent folder
|
||||
SELECT
|
||||
da.id,
|
||||
da.org_id,
|
||||
da.dashboard_id,
|
||||
da.user_id,
|
||||
da.team_id,
|
||||
da.permission,
|
||||
da.role,
|
||||
da.created,
|
||||
da.updated,
|
||||
u.login AS user_login,
|
||||
u.email AS user_email,
|
||||
ug.name AS team,
|
||||
ug.email AS team_email,
|
||||
d.title,
|
||||
d.slug,
|
||||
d.uid,
|
||||
d.is_folder,
|
||||
CASE WHEN (da.dashboard_id = -1 AND d.folder_id > 0) OR da.dashboard_id = d.folder_id THEN ` + d.store.GetDialect().BooleanStr(true) + ` ELSE ` + falseStr + ` END AS inherited
|
||||
FROM dashboard as d
|
||||
LEFT JOIN dashboard folder on folder.id = d.folder_id
|
||||
LEFT JOIN dashboard_acl AS da ON
|
||||
da.dashboard_id = d.id OR
|
||||
da.dashboard_id = d.folder_id OR
|
||||
(
|
||||
-- include default permissions -->
|
||||
da.org_id = -1 AND (
|
||||
(folder.id IS NOT NULL AND folder.has_acl = ` + falseStr + `) OR
|
||||
(folder.id IS NULL AND d.has_acl = ` + falseStr + `)
|
||||
)
|
||||
)
|
||||
LEFT JOIN ` + d.store.GetDialect().Quote("user") + ` AS u ON u.id = da.user_id
|
||||
LEFT JOIN team ug on ug.id = da.team_id
|
||||
WHERE d.org_id = ? AND d.id = ? AND da.id IS NOT NULL
|
||||
ORDER BY da.id ASC
|
||||
`
|
||||
|
||||
return dbSession.SQL(rawSQL, query.OrgID, query.DashboardID).Find(&queryResult)
|
||||
})
|
||||
|
||||
if outerErr != nil {
|
||||
return nil, outerErr
|
||||
}
|
||||
|
||||
for _, p := range queryResult {
|
||||
p.PermissionName = p.Permission.String()
|
||||
}
|
||||
|
||||
return queryResult, nil
|
||||
}
|
||||
|
||||
func (d *dashboardStore) DeleteACLByUser(ctx context.Context, userID int64) error {
|
||||
return d.store.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
||||
var rawSQL = "DELETE FROM dashboard_acl WHERE user_id = ?"
|
||||
_, err := sess.Exec(rawSQL, userID)
|
||||
return err
|
||||
})
|
||||
}
|
@ -1,299 +0,0 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/org/orgimpl"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotaimpl"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
||||
"github.com/grafana/grafana/pkg/services/team/teamimpl"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/services/user/userimpl"
|
||||
)
|
||||
|
||||
func TestIntegrationDashboardACLDataAccess(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
var sqlStore *sqlstore.SQLStore
|
||||
var currentUser user.User
|
||||
var savedFolder, childDash *dashboards.Dashboard
|
||||
var dashboardStore dashboards.Store
|
||||
|
||||
setup := func(t *testing.T) int64 {
|
||||
sqlStore = db.InitTestDB(t)
|
||||
quotaService := quotatest.New(false, nil)
|
||||
var err error
|
||||
dashboardStore, err = ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore), quotaService)
|
||||
require.NoError(t, err)
|
||||
currentUser = createUser(t, sqlStore, "viewer", "Viewer", false)
|
||||
savedFolder = insertTestDashboard(t, dashboardStore, "1 test dash folder", 1, 0, "", true, "prod", "webapp")
|
||||
childDash = insertTestDashboard(t, dashboardStore, "2 test dash", 1, savedFolder.ID, savedFolder.UID, false, "prod", "webapp")
|
||||
return currentUser.OrgID
|
||||
}
|
||||
|
||||
t.Run("Dashboard permission with userId and teamId set to 0", func(t *testing.T) {
|
||||
orgID := setup(t)
|
||||
err := updateDashboardACL(t, dashboardStore, savedFolder.ID, dashboards.DashboardACL{
|
||||
OrgID: orgID,
|
||||
DashboardID: savedFolder.ID,
|
||||
Permission: dashboards.PERMISSION_EDIT,
|
||||
})
|
||||
require.Equal(t, dashboards.ErrDashboardACLInfoMissing, err)
|
||||
})
|
||||
|
||||
t.Run("Folder acl should include default acl", func(t *testing.T) {
|
||||
orgID := setup(t)
|
||||
query := dashboards.GetDashboardACLInfoListQuery{DashboardID: savedFolder.ID, OrgID: orgID}
|
||||
|
||||
queryResult, err := dashboardStore.GetDashboardACLInfoList(context.Background(), &query)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, 2, len(queryResult))
|
||||
defaultPermissionsId := int64(-1)
|
||||
require.Equal(t, defaultPermissionsId, queryResult[0].DashboardID)
|
||||
require.Equal(t, org.RoleViewer, *queryResult[0].Role)
|
||||
require.False(t, queryResult[0].Inherited)
|
||||
require.Equal(t, defaultPermissionsId, queryResult[1].DashboardID)
|
||||
require.Equal(t, org.RoleEditor, *queryResult[1].Role)
|
||||
require.False(t, queryResult[1].Inherited)
|
||||
})
|
||||
|
||||
t.Run("Dashboard acl should include acl for parent folder", func(t *testing.T) {
|
||||
orgID := setup(t)
|
||||
query := dashboards.GetDashboardACLInfoListQuery{DashboardID: childDash.ID, OrgID: orgID}
|
||||
|
||||
queryResult, err := dashboardStore.GetDashboardACLInfoList(context.Background(), &query)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, 2, len(queryResult))
|
||||
defaultPermissionsId := int64(-1)
|
||||
require.Equal(t, defaultPermissionsId, queryResult[0].DashboardID)
|
||||
require.Equal(t, org.RoleViewer, *queryResult[0].Role)
|
||||
require.True(t, queryResult[0].Inherited)
|
||||
require.Equal(t, defaultPermissionsId, queryResult[1].DashboardID)
|
||||
require.Equal(t, org.RoleEditor, *queryResult[1].Role)
|
||||
require.True(t, queryResult[1].Inherited)
|
||||
})
|
||||
|
||||
t.Run("Folder with removed default permissions returns no acl items", func(t *testing.T) {
|
||||
orgID := setup(t)
|
||||
err := dashboardStore.UpdateDashboardACL(context.Background(), savedFolder.ID, nil)
|
||||
require.Nil(t, err)
|
||||
|
||||
query := dashboards.GetDashboardACLInfoListQuery{DashboardID: childDash.ID, OrgID: orgID}
|
||||
queryResult, err := dashboardStore.GetDashboardACLInfoList(context.Background(), &query)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, 0, len(queryResult))
|
||||
})
|
||||
|
||||
t.Run("Given a dashboard folder and a user", func(t *testing.T) {
|
||||
t.Run("Given dashboard folder permission", func(t *testing.T) {
|
||||
orgID := setup(t)
|
||||
err := updateDashboardACL(t, dashboardStore, savedFolder.ID, dashboards.DashboardACL{
|
||||
OrgID: orgID,
|
||||
UserID: currentUser.ID,
|
||||
DashboardID: savedFolder.ID,
|
||||
Permission: dashboards.PERMISSION_EDIT,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
t.Run("When reading dashboard acl should include acl for parent folder", func(t *testing.T) {
|
||||
query := dashboards.GetDashboardACLInfoListQuery{DashboardID: childDash.ID, OrgID: orgID}
|
||||
|
||||
queryResult, err := dashboardStore.GetDashboardACLInfoList(context.Background(), &query)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, 1, len(queryResult))
|
||||
require.Equal(t, savedFolder.ID, queryResult[0].DashboardID)
|
||||
})
|
||||
|
||||
t.Run("Given child dashboard permission", func(t *testing.T) {
|
||||
err := updateDashboardACL(t, dashboardStore, childDash.ID, dashboards.DashboardACL{
|
||||
OrgID: orgID,
|
||||
UserID: currentUser.ID,
|
||||
DashboardID: childDash.ID,
|
||||
Permission: dashboards.PERMISSION_EDIT,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
t.Run("When reading dashboard acl should include acl for parent folder and child", func(t *testing.T) {
|
||||
query := dashboards.GetDashboardACLInfoListQuery{OrgID: orgID, DashboardID: childDash.ID}
|
||||
|
||||
queryResult, err := dashboardStore.GetDashboardACLInfoList(context.Background(), &query)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, 2, len(queryResult))
|
||||
require.Equal(t, savedFolder.ID, queryResult[0].DashboardID)
|
||||
require.True(t, queryResult[0].Inherited)
|
||||
require.Equal(t, childDash.ID, queryResult[1].DashboardID)
|
||||
require.False(t, queryResult[1].Inherited)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Reading dashboard acl should include default acl for parent folder and the child acl", func(t *testing.T) {
|
||||
orgID := setup(t)
|
||||
err := updateDashboardACL(t, dashboardStore, childDash.ID, dashboards.DashboardACL{
|
||||
OrgID: 1,
|
||||
UserID: currentUser.ID,
|
||||
DashboardID: childDash.ID,
|
||||
Permission: dashboards.PERMISSION_EDIT,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
query := dashboards.GetDashboardACLInfoListQuery{OrgID: orgID, DashboardID: childDash.ID}
|
||||
|
||||
queryResult, err := dashboardStore.GetDashboardACLInfoList(context.Background(), &query)
|
||||
require.Nil(t, err)
|
||||
|
||||
defaultPermissionsId := int64(-1)
|
||||
require.Equal(t, 3, len(queryResult))
|
||||
require.Equal(t, defaultPermissionsId, queryResult[0].DashboardID)
|
||||
require.Equal(t, org.RoleViewer, *queryResult[0].Role)
|
||||
require.True(t, queryResult[0].Inherited)
|
||||
require.Equal(t, defaultPermissionsId, queryResult[1].DashboardID)
|
||||
require.Equal(t, org.RoleEditor, *queryResult[1].Role)
|
||||
require.True(t, queryResult[1].Inherited)
|
||||
require.Equal(t, childDash.ID, queryResult[2].DashboardID)
|
||||
require.False(t, queryResult[2].Inherited)
|
||||
})
|
||||
|
||||
t.Run("Add and delete dashboard permission", func(t *testing.T) {
|
||||
orgID := setup(t)
|
||||
err := updateDashboardACL(t, dashboardStore, savedFolder.ID, dashboards.DashboardACL{
|
||||
OrgID: 1,
|
||||
UserID: currentUser.ID,
|
||||
DashboardID: savedFolder.ID,
|
||||
Permission: dashboards.PERMISSION_EDIT,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
q1 := &dashboards.GetDashboardACLInfoListQuery{DashboardID: savedFolder.ID, OrgID: orgID}
|
||||
q1Result, err := dashboardStore.GetDashboardACLInfoList(context.Background(), q1)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, savedFolder.ID, q1Result[0].DashboardID)
|
||||
require.Equal(t, dashboards.PERMISSION_EDIT, q1Result[0].Permission)
|
||||
require.Equal(t, "Edit", q1Result[0].PermissionName)
|
||||
require.Equal(t, currentUser.ID, q1Result[0].UserID)
|
||||
require.Equal(t, currentUser.Login, q1Result[0].UserLogin)
|
||||
require.Equal(t, currentUser.Email, q1Result[0].UserEmail)
|
||||
|
||||
err = updateDashboardACL(t, dashboardStore, savedFolder.ID)
|
||||
require.Nil(t, err)
|
||||
|
||||
q3 := &dashboards.GetDashboardACLInfoListQuery{DashboardID: savedFolder.ID, OrgID: orgID}
|
||||
q3Result, err := dashboardStore.GetDashboardACLInfoList(context.Background(), q3)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 0, len(q3Result))
|
||||
})
|
||||
|
||||
t.Run("Should be able to add a user permission for a team", func(t *testing.T) {
|
||||
orgID := setup(t)
|
||||
teamSvc := teamimpl.ProvideService(sqlStore, sqlStore.Cfg)
|
||||
team1, err := teamSvc.CreateTeam("group1 name", "", 1)
|
||||
require.Nil(t, err)
|
||||
|
||||
err = updateDashboardACL(t, dashboardStore, savedFolder.ID, dashboards.DashboardACL{
|
||||
OrgID: 1,
|
||||
TeamID: team1.ID,
|
||||
DashboardID: savedFolder.ID,
|
||||
Permission: dashboards.PERMISSION_EDIT,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
q1 := &dashboards.GetDashboardACLInfoListQuery{DashboardID: savedFolder.ID, OrgID: orgID}
|
||||
q1Result, err := dashboardStore.GetDashboardACLInfoList(context.Background(), q1)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, savedFolder.ID, q1Result[0].DashboardID)
|
||||
require.Equal(t, dashboards.PERMISSION_EDIT, q1Result[0].Permission)
|
||||
require.Equal(t, team1.ID, q1Result[0].TeamID)
|
||||
})
|
||||
|
||||
t.Run("Should be able to update an existing permission for a team", func(t *testing.T) {
|
||||
orgID := setup(t)
|
||||
teamSvc := teamimpl.ProvideService(sqlStore, sqlStore.Cfg)
|
||||
team1, err := teamSvc.CreateTeam("group1 name", "", 1)
|
||||
require.Nil(t, err)
|
||||
err = updateDashboardACL(t, dashboardStore, savedFolder.ID, dashboards.DashboardACL{
|
||||
OrgID: 1,
|
||||
TeamID: team1.ID,
|
||||
DashboardID: savedFolder.ID,
|
||||
Permission: dashboards.PERMISSION_ADMIN,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
q3 := &dashboards.GetDashboardACLInfoListQuery{DashboardID: savedFolder.ID, OrgID: orgID}
|
||||
q3Result, err := dashboardStore.GetDashboardACLInfoList(context.Background(), q3)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, 1, len(q3Result))
|
||||
require.Equal(t, savedFolder.ID, q3Result[0].DashboardID)
|
||||
require.Equal(t, dashboards.PERMISSION_ADMIN, q3Result[0].Permission)
|
||||
require.Equal(t, team1.ID, q3Result[0].TeamID)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Default permissions for root folder dashboards", func(t *testing.T) {
|
||||
orgID := setup(t)
|
||||
var rootFolderId int64 = 0
|
||||
//sqlStore := db.InitTestDB(t)
|
||||
|
||||
query := dashboards.GetDashboardACLInfoListQuery{DashboardID: rootFolderId, OrgID: orgID}
|
||||
|
||||
queryResult, err := dashboardStore.GetDashboardACLInfoList(context.Background(), &query)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Equal(t, 2, len(queryResult))
|
||||
defaultPermissionsId := int64(-1)
|
||||
require.Equal(t, defaultPermissionsId, queryResult[0].DashboardID)
|
||||
require.Equal(t, org.RoleViewer, *queryResult[0].Role)
|
||||
require.False(t, queryResult[0].Inherited)
|
||||
require.Equal(t, defaultPermissionsId, queryResult[1].DashboardID)
|
||||
require.Equal(t, org.RoleEditor, *queryResult[1].Role)
|
||||
require.False(t, queryResult[1].Inherited)
|
||||
})
|
||||
|
||||
t.Run("Delete acl by user", func(t *testing.T) {
|
||||
setup(t)
|
||||
err := dashboardStore.DeleteACLByUser(context.Background(), currentUser.ID)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func createUser(t *testing.T, sqlStore *sqlstore.SQLStore, name string, role string, isAdmin bool) user.User {
|
||||
t.Helper()
|
||||
sqlStore.Cfg.AutoAssignOrg = true
|
||||
sqlStore.Cfg.AutoAssignOrgId = 1
|
||||
sqlStore.Cfg.AutoAssignOrgRole = role
|
||||
|
||||
qs := quotaimpl.ProvideService(sqlStore, sqlStore.Cfg)
|
||||
orgService, err := orgimpl.ProvideService(sqlStore, sqlStore.Cfg, qs)
|
||||
require.NoError(t, err)
|
||||
usrSvc, err := userimpl.ProvideService(sqlStore, orgService, sqlStore.Cfg, nil, nil, qs, supportbundlestest.NewFakeBundleService())
|
||||
require.NoError(t, err)
|
||||
|
||||
o, err := orgService.CreateWithMember(context.Background(), &org.CreateOrgCommand{Name: fmt.Sprintf("test org %d", time.Now().UnixNano())})
|
||||
require.NoError(t, err)
|
||||
|
||||
currentUserCmd := user.CreateUserCommand{Login: name, Email: name + "@test.com", Name: "a " + name, IsAdmin: isAdmin, OrgID: o.ID}
|
||||
currentUser, err := usrSvc.Create(context.Background(), ¤tUserCmd)
|
||||
require.NoError(t, err)
|
||||
orgs, err := orgService.GetUserOrgList(context.Background(), &org.GetUserOrgListQuery{UserID: currentUser.ID})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, org.RoleType(role), orgs[0].Role)
|
||||
require.Equal(t, o.ID, orgs[0].OrgID)
|
||||
return *currentUser
|
||||
}
|
@ -171,36 +171,6 @@ func (d *dashboardStore) SaveDashboard(ctx context.Context, cmd dashboards.SaveD
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (d *dashboardStore) UpdateDashboardACL(ctx context.Context, dashboardID int64, items []*dashboards.DashboardACL) error {
|
||||
return d.store.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
||||
// delete existing items
|
||||
_, err := sess.Exec("DELETE FROM dashboard_acl WHERE dashboard_id=?", dashboardID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting from dashboard_acl failed: %w", err)
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
if item.UserID == 0 && item.TeamID == 0 && (item.Role == nil || !item.Role.IsValid()) {
|
||||
return dashboards.ErrDashboardACLInfoMissing
|
||||
}
|
||||
|
||||
if item.DashboardID == 0 {
|
||||
return dashboards.ErrDashboardPermissionDashboardEmpty
|
||||
}
|
||||
|
||||
sess.Nullable("user_id", "team_id")
|
||||
if _, err := sess.Insert(item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Update dashboard HasACL flag
|
||||
dashboard := dashboards.Dashboard{HasACL: true}
|
||||
_, err = sess.Cols("has_acl").Where("id=?", dashboardID).Update(&dashboard)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (d *dashboardStore) SaveAlerts(ctx context.Context, dashID int64, alerts []*alertmodels.Alert) error {
|
||||
return d.store.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
||||
existingAlerts, err := GetAlertsByDashboardId2(dashID, sess)
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -23,6 +24,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/guardian"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/org/orgimpl"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotaimpl"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
||||
"github.com/grafana/grafana/pkg/services/sqlstore"
|
||||
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
||||
@ -450,3 +452,28 @@ func moveDashboard(t *testing.T, dashboardStore dashboards.Store, orgId int64, d
|
||||
|
||||
return dash
|
||||
}
|
||||
|
||||
func createUser(t *testing.T, sqlStore *sqlstore.SQLStore, name string, role string, isAdmin bool) user.User {
|
||||
t.Helper()
|
||||
sqlStore.Cfg.AutoAssignOrg = true
|
||||
sqlStore.Cfg.AutoAssignOrgId = 1
|
||||
sqlStore.Cfg.AutoAssignOrgRole = role
|
||||
|
||||
qs := quotaimpl.ProvideService(sqlStore, sqlStore.Cfg)
|
||||
orgService, err := orgimpl.ProvideService(sqlStore, sqlStore.Cfg, qs)
|
||||
require.NoError(t, err)
|
||||
usrSvc, err := userimpl.ProvideService(sqlStore, orgService, sqlStore.Cfg, nil, nil, qs, supportbundlestest.NewFakeBundleService())
|
||||
require.NoError(t, err)
|
||||
|
||||
o, err := orgService.CreateWithMember(context.Background(), &org.CreateOrgCommand{Name: fmt.Sprintf("test org %d", time.Now().UnixNano())})
|
||||
require.NoError(t, err)
|
||||
|
||||
currentUserCmd := user.CreateUserCommand{Login: name, Email: name + "@test.com", Name: "a " + name, IsAdmin: isAdmin, OrgID: o.ID}
|
||||
currentUser, err := usrSvc.Create(context.Background(), ¤tUserCmd)
|
||||
require.NoError(t, err)
|
||||
orgs, err := orgService.GetUserOrgList(context.Background(), &org.GetUserOrgListQuery{UserID: currentUser.ID})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, org.RoleType(role), orgs[0].Role)
|
||||
require.Equal(t, o.ID, orgs[0].OrgID)
|
||||
return *currentUser
|
||||
}
|
||||
|
@ -1183,21 +1183,6 @@ func insertTestDashboardForPlugin(t *testing.T, dashboardStore dashboards.Store,
|
||||
return dash
|
||||
}
|
||||
|
||||
func updateDashboardACL(t *testing.T, dashboardStore dashboards.Store, dashboardID int64,
|
||||
items ...dashboards.DashboardACL) error {
|
||||
t.Helper()
|
||||
|
||||
var itemPtrs []*dashboards.DashboardACL
|
||||
for _, it := range items {
|
||||
item := it
|
||||
item.Created = time.Now()
|
||||
item.Updated = time.Now()
|
||||
itemPtrs = append(itemPtrs, &item)
|
||||
}
|
||||
|
||||
return dashboardStore.UpdateDashboardACL(context.Background(), dashboardID, itemPtrs)
|
||||
}
|
||||
|
||||
// testSearchDashboards is a (near) copy of the dashboard service
|
||||
// SearchDashboards, which is a wrapper around FindDashboards.
|
||||
func testSearchDashboards(d dashboards.Store, query *dashboards.FindPersistedDashboardsQuery) (model.HitList, error) {
|
||||
|
@ -473,12 +473,6 @@ func (dto *DashboardACLInfoDTO) IsDuplicateOf(other *DashboardACLInfoDTO) bool {
|
||||
return dto.hasSameRoleAs(other) || dto.hasSameUserAs(other) || dto.hasSameTeamAs(other)
|
||||
}
|
||||
|
||||
// QUERIES
|
||||
type GetDashboardACLInfoListQuery struct {
|
||||
DashboardID int64
|
||||
OrgID int64
|
||||
}
|
||||
|
||||
type FindPersistedDashboardsQuery struct {
|
||||
Title string
|
||||
OrgId int64
|
||||
|
@ -221,10 +221,6 @@ func resolveUserID(user identity.Requester, log log.Logger) (int64, error) {
|
||||
return userID, nil
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) UpdateDashboardACL(ctx context.Context, uid int64, items []*dashboards.DashboardACL) error {
|
||||
return dr.dashboardStore.UpdateDashboardACL(ctx, uid, items)
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) DeleteOrphanedProvisionedDashboards(ctx context.Context, cmd *dashboards.DeleteOrphanedProvisionedDashboardsCommand) error {
|
||||
return dr.dashboardStore.DeleteOrphanedProvisionedDashboards(ctx, cmd)
|
||||
}
|
||||
@ -584,18 +580,10 @@ func makeQueryResult(query *dashboards.FindPersistedDashboardsQuery, res []dashb
|
||||
return hitList
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) GetDashboardACLInfoList(ctx context.Context, query *dashboards.GetDashboardACLInfoListQuery) ([]*dashboards.DashboardACLInfoDTO, error) {
|
||||
return dr.dashboardStore.GetDashboardACLInfoList(ctx, query)
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) GetDashboardTags(ctx context.Context, query *dashboards.GetDashboardTagsQuery) ([]*dashboards.DashboardTagCloudItem, error) {
|
||||
return dr.dashboardStore.GetDashboardTags(ctx, query)
|
||||
}
|
||||
|
||||
func (dr *DashboardServiceImpl) DeleteACLByUser(ctx context.Context, userID int64) error {
|
||||
return dr.dashboardStore.DeleteACLByUser(ctx, userID)
|
||||
}
|
||||
|
||||
func (dr DashboardServiceImpl) CountInFolder(ctx context.Context, orgID int64, folderUID string, u identity.Requester) (int64, error) {
|
||||
folder, err := dr.folderService.Get(ctx, &folder.GetFolderQuery{UID: &folderUID, OrgID: orgID, SignedInUser: u})
|
||||
if err != nil {
|
||||
|
@ -219,14 +219,6 @@ func TestDashboardService(t *testing.T) {
|
||||
err := service.DeleteDashboard(context.Background(), 1, 1)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
// t.Run("Delete ACL by user", func(t *testing.T) {
|
||||
// fakeStore := dashboards.FakeDashboardStore{}
|
||||
// args := 1
|
||||
// fakeStore.On("DeleteACLByUser", mock.Anything, args).Return(nil).Once()
|
||||
// err := service.DeleteACLByUser(context.Background(), 1)
|
||||
// require.NoError(t, err)
|
||||
// })
|
||||
})
|
||||
|
||||
t.Run("Count dashboards in folder", func(t *testing.T) {
|
||||
@ -248,41 +240,4 @@ func TestDashboardService(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Delete user by acl", func(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
fakeStore.On("DeleteACLByUser", mock.Anything, mock.AnythingOfType("int64")).Return(nil)
|
||||
defer fakeStore.AssertExpectations(t)
|
||||
|
||||
service := &DashboardServiceImpl{
|
||||
cfg: setting.NewCfg(),
|
||||
log: log.New("test.logger"),
|
||||
dashboardStore: &fakeStore,
|
||||
dashAlertExtractor: &dummyDashAlertExtractor{},
|
||||
}
|
||||
err := service.DeleteACLByUser(context.Background(), 1)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("When org user is deleted", func(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
fakeStore.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardACLInfoListQuery")).Return(nil, nil)
|
||||
t.Run("Should remove dependent permissions for deleted org user", func(t *testing.T) {
|
||||
permQuery := &dashboards.GetDashboardACLInfoListQuery{DashboardID: 1, OrgID: 1}
|
||||
|
||||
permQueryResult, err := fakeStore.GetDashboardACLInfoList(context.Background(), permQuery)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, len(permQueryResult), 0)
|
||||
})
|
||||
|
||||
t.Run("Should not remove dashboard permissions for same user in another org", func(t *testing.T) {
|
||||
fakeStore := dashboards.FakeDashboardStore{}
|
||||
fakeStore.On("GetDashboardACLInfoList", mock.Anything, mock.AnythingOfType("*dashboards.GetDashboardACLInfoListQuery")).Return(nil, nil)
|
||||
permQuery := &dashboards.GetDashboardACLInfoListQuery{DashboardID: 2, OrgID: 3}
|
||||
|
||||
_, err := fakeStore.GetDashboardACLInfoList(context.Background(), permQuery)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -66,20 +66,6 @@ func (_m *FakeDashboardStore) CountDashboardsInFolder(ctx context.Context, reque
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// DeleteACLByUser provides a mock function with given fields: _a0, _a1
|
||||
func (_m *FakeDashboardStore) DeleteACLByUser(_a0 context.Context, _a1 int64) error {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64) error); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// DeleteDashboard provides a mock function with given fields: ctx, cmd
|
||||
func (_m *FakeDashboardStore) DeleteDashboard(ctx context.Context, cmd *DeleteDashboardCommand) error {
|
||||
ret := _m.Called(ctx, cmd)
|
||||
@ -174,32 +160,6 @@ func (_m *FakeDashboardStore) GetDashboard(ctx context.Context, query *GetDashbo
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetDashboardACLInfoList provides a mock function with given fields: ctx, query
|
||||
func (_m *FakeDashboardStore) GetDashboardACLInfoList(ctx context.Context, query *GetDashboardACLInfoListQuery) ([]*DashboardACLInfoDTO, error) {
|
||||
ret := _m.Called(ctx, query)
|
||||
|
||||
var r0 []*DashboardACLInfoDTO
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *GetDashboardACLInfoListQuery) ([]*DashboardACLInfoDTO, error)); ok {
|
||||
return rf(ctx, query)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *GetDashboardACLInfoListQuery) []*DashboardACLInfoDTO); ok {
|
||||
r0 = rf(ctx, query)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*DashboardACLInfoDTO)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *GetDashboardACLInfoListQuery) error); ok {
|
||||
r1 = rf(ctx, query)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetDashboardTags provides a mock function with given fields: ctx, query
|
||||
func (_m *FakeDashboardStore) GetDashboardTags(ctx context.Context, query *GetDashboardTagsQuery) ([]*DashboardTagCloudItem, error) {
|
||||
ret := _m.Called(ctx, query)
|
||||
@ -462,20 +422,6 @@ func (_m *FakeDashboardStore) UnprovisionDashboard(ctx context.Context, id int64
|
||||
return r0
|
||||
}
|
||||
|
||||
// UpdateDashboardACL provides a mock function with given fields: ctx, uid, items
|
||||
func (_m *FakeDashboardStore) UpdateDashboardACL(ctx context.Context, uid int64, items []*DashboardACL) error {
|
||||
ret := _m.Called(ctx, uid, items)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(context.Context, int64, []*DashboardACL) error); ok {
|
||||
r0 = rf(ctx, uid, items)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// ValidateDashboardBeforeSave provides a mock function with given fields: ctx, dashboard, overwrite
|
||||
func (_m *FakeDashboardStore) ValidateDashboardBeforeSave(ctx context.Context, dashboard *Dashboard, overwrite bool) (bool, error) {
|
||||
ret := _m.Called(ctx, dashboard, overwrite)
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -351,30 +350,6 @@ func TestIntegrationTeamCommandsAndQueries(t *testing.T) {
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Should be able to remove a group with users and permissions", func(t *testing.T) {
|
||||
groupID := team2.ID
|
||||
err := teamSvc.AddTeamMember(userIds[1], testOrgID, groupID, false, 0)
|
||||
require.NoError(t, err)
|
||||
err = teamSvc.AddTeamMember(userIds[2], testOrgID, groupID, false, 0)
|
||||
require.NoError(t, err)
|
||||
err = updateDashboardACL(t, sqlStore, 1, &dashboards.DashboardACL{
|
||||
DashboardID: 1, OrgID: testOrgID, Permission: dashboards.PERMISSION_EDIT, TeamID: groupID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
err = teamSvc.DeleteTeam(context.Background(), &team.DeleteTeamCommand{OrgID: testOrgID, ID: groupID})
|
||||
require.NoError(t, err)
|
||||
|
||||
query := &team.GetTeamByIDQuery{OrgID: testOrgID, ID: groupID}
|
||||
_, err = teamSvc.GetTeamByID(context.Background(), query)
|
||||
require.Equal(t, err, team.ErrTeamNotFound)
|
||||
|
||||
permQuery := &dashboards.GetDashboardACLInfoListQuery{DashboardID: 1, OrgID: testOrgID}
|
||||
permQueryResult, err := getDashboardACLInfoList(sqlStore, permQuery)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, len(permQueryResult), 0)
|
||||
})
|
||||
|
||||
t.Run("Should not return hidden users in team member count", func(t *testing.T) {
|
||||
sqlStore = db.InitTestDB(t)
|
||||
setup()
|
||||
@ -646,123 +621,3 @@ func hasWildcardScope(user identity.Requester, action string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO: Use FakeDashboardStore when org has its own service
|
||||
func updateDashboardACL(t *testing.T, sqlStore *sqlstore.SQLStore, dashboardID int64, items ...*dashboards.DashboardACL) error {
|
||||
t.Helper()
|
||||
|
||||
err := sqlStore.WithDbSession(context.Background(), func(sess *db.Session) error {
|
||||
_, err := sess.Exec("DELETE FROM dashboard_acl WHERE dashboard_id=?", dashboardID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting from dashboard_acl failed: %w", err)
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
item.Created = time.Now()
|
||||
item.Updated = time.Now()
|
||||
if item.UserID == 0 && item.TeamID == 0 && (item.Role == nil || !item.Role.IsValid()) {
|
||||
return dashboards.ErrDashboardACLInfoMissing
|
||||
}
|
||||
|
||||
if item.DashboardID == 0 {
|
||||
return dashboards.ErrDashboardPermissionDashboardEmpty
|
||||
}
|
||||
|
||||
sess.Nullable("user_id", "team_id")
|
||||
if _, err := sess.Insert(item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Update dashboard HasACL flag
|
||||
dashboard := dashboards.Dashboard{HasACL: true}
|
||||
_, err = sess.Cols("has_acl").Where("id=?", dashboardID).Update(&dashboard)
|
||||
return err
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// This function was copied from pkg/services/dashboards/database to circumvent
|
||||
// import cycles. When this org-related code is refactored into a service the
|
||||
// tests can the real GetDashboardACLInfoList functions
|
||||
func getDashboardACLInfoList(s *sqlstore.SQLStore, query *dashboards.GetDashboardACLInfoListQuery) ([]*dashboards.DashboardACLInfoDTO, error) {
|
||||
queryResult := make([]*dashboards.DashboardACLInfoDTO, 0)
|
||||
outerErr := s.WithDbSession(context.Background(), func(dbSession *db.Session) error {
|
||||
falseStr := s.GetDialect().BooleanStr(false)
|
||||
|
||||
if query.DashboardID == 0 {
|
||||
sql := `SELECT
|
||||
da.id,
|
||||
da.org_id,
|
||||
da.dashboard_id,
|
||||
da.user_id,
|
||||
da.team_id,
|
||||
da.permission,
|
||||
da.role,
|
||||
da.created,
|
||||
da.updated,
|
||||
'' as user_login,
|
||||
'' as user_email,
|
||||
'' as team,
|
||||
'' as title,
|
||||
'' as slug,
|
||||
'' as uid,` +
|
||||
falseStr + ` AS is_folder,` +
|
||||
falseStr + ` AS inherited
|
||||
FROM dashboard_acl as da
|
||||
WHERE da.dashboard_id = -1`
|
||||
return dbSession.SQL(sql).Find(&queryResult)
|
||||
}
|
||||
|
||||
rawSQL := `
|
||||
-- get permissions for the dashboard and its parent folder
|
||||
SELECT
|
||||
da.id,
|
||||
da.org_id,
|
||||
da.dashboard_id,
|
||||
da.user_id,
|
||||
da.team_id,
|
||||
da.permission,
|
||||
da.role,
|
||||
da.created,
|
||||
da.updated,
|
||||
u.login AS user_login,
|
||||
u.email AS user_email,
|
||||
ug.name AS team,
|
||||
ug.email AS team_email,
|
||||
d.title,
|
||||
d.slug,
|
||||
d.uid,
|
||||
d.is_folder,
|
||||
CASE WHEN (da.dashboard_id = -1 AND d.folder_id > 0) OR da.dashboard_id = d.folder_id THEN ` + s.GetDialect().BooleanStr(true) + ` ELSE ` + falseStr + ` END AS inherited
|
||||
FROM dashboard as d
|
||||
LEFT JOIN dashboard folder on folder.id = d.folder_id
|
||||
LEFT JOIN dashboard_acl AS da ON
|
||||
da.dashboard_id = d.id OR
|
||||
da.dashboard_id = d.folder_id OR
|
||||
(
|
||||
-- include default permissions -->
|
||||
da.org_id = -1 AND (
|
||||
(folder.id IS NOT NULL AND folder.has_acl = ` + falseStr + `) OR
|
||||
(folder.id IS NULL AND d.has_acl = ` + falseStr + `)
|
||||
)
|
||||
)
|
||||
LEFT JOIN ` + s.GetDialect().Quote("user") + ` AS u ON u.id = da.user_id
|
||||
LEFT JOIN team ug on ug.id = da.team_id
|
||||
WHERE d.org_id = ? AND d.id = ? AND da.id IS NOT NULL
|
||||
ORDER BY da.id ASC
|
||||
`
|
||||
|
||||
return dbSession.SQL(rawSQL, query.OrgID, query.DashboardID).Find(&queryResult)
|
||||
})
|
||||
|
||||
if outerErr != nil {
|
||||
return nil, outerErr
|
||||
}
|
||||
|
||||
for _, p := range queryResult {
|
||||
p.PermissionName = p.Permission.String()
|
||||
}
|
||||
|
||||
return queryResult, nil
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/org/orgimpl"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotaimpl"
|
||||
@ -378,12 +377,6 @@ func TestIntegrationUserDataAccess(t *testing.T) {
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
err = updateDashboardACL(t, ss, 1, &dashboards.DashboardACL{
|
||||
DashboardID: 1, OrgID: users[0].OrgID, UserID: users[1].ID,
|
||||
Permission: dashboards.PERMISSION_EDIT,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
ss.CacheService.Flush()
|
||||
|
||||
query := &user.GetSignedInUserQuery{OrgID: users[1].OrgID, UserID: users[1].ID}
|
||||
@ -526,22 +519,10 @@ func TestIntegrationUserDataAccess(t *testing.T) {
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
err = updateDashboardACL(t, ss, 1, &dashboards.DashboardACL{
|
||||
DashboardID: 1, OrgID: users[0].OrgID, UserID: users[1].ID,
|
||||
Permission: dashboards.PERMISSION_EDIT,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
// When the user is deleted
|
||||
err = userStore.Delete(context.Background(), users[1].ID)
|
||||
require.Nil(t, err)
|
||||
|
||||
permQuery := &dashboards.GetDashboardACLInfoListQuery{DashboardID: 1, OrgID: users[0].OrgID}
|
||||
permQueryResult, err := userStore.getDashboardACLInfoList(permQuery)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Len(t, permQueryResult, 0)
|
||||
|
||||
// A user is an org member and has been assigned permissions
|
||||
// Re-init DB
|
||||
ss = db.InitTestDB(t)
|
||||
@ -560,12 +541,6 @@ func TestIntegrationUserDataAccess(t *testing.T) {
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
err = updateDashboardACL(t, ss, 1, &dashboards.DashboardACL{
|
||||
DashboardID: 1, OrgID: users[0].OrgID, UserID: users[1].ID,
|
||||
Permission: dashboards.PERMISSION_EDIT,
|
||||
})
|
||||
require.Nil(t, err)
|
||||
|
||||
ss.CacheService.Flush()
|
||||
|
||||
query3 := &user.GetSignedInUserQuery{OrgID: users[1].OrgID, UserID: users[1].ID}
|
||||
@ -591,12 +566,6 @@ func TestIntegrationUserDataAccess(t *testing.T) {
|
||||
// the user is deleted
|
||||
err = userStore.Delete(context.Background(), users[1].ID)
|
||||
require.Nil(t, err)
|
||||
|
||||
permQuery = &dashboards.GetDashboardACLInfoListQuery{DashboardID: 1, OrgID: users[0].OrgID}
|
||||
permQueryResult, err = userStore.getDashboardACLInfoList(permQuery)
|
||||
require.Nil(t, err)
|
||||
|
||||
require.Len(t, permQueryResult, 0)
|
||||
})
|
||||
|
||||
t.Run("Testing DB - return list of users that the SignedInUser has permission to read", func(t *testing.T) {
|
||||
@ -947,41 +916,6 @@ func createFiveTestUsers(t *testing.T, svc user.Service, fn func(i int) *user.Cr
|
||||
return users
|
||||
}
|
||||
|
||||
// TODO: Use FakeDashboardStore when org has its own service
|
||||
func updateDashboardACL(t *testing.T, sqlStore db.DB, dashboardID int64, items ...*dashboards.DashboardACL) error {
|
||||
t.Helper()
|
||||
|
||||
err := sqlStore.WithDbSession(context.Background(), func(sess *db.Session) error {
|
||||
_, err := sess.Exec("DELETE FROM dashboard_acl WHERE dashboard_id=?", dashboardID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting from dashboard_acl failed: %w", err)
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
item.Created = time.Now()
|
||||
item.Updated = time.Now()
|
||||
if item.UserID == 0 && item.TeamID == 0 && (item.Role == nil || !item.Role.IsValid()) {
|
||||
return dashboards.ErrDashboardACLInfoMissing
|
||||
}
|
||||
|
||||
if item.DashboardID == 0 {
|
||||
return dashboards.ErrDashboardPermissionDashboardEmpty
|
||||
}
|
||||
|
||||
sess.Nullable("user_id", "team_id")
|
||||
if _, err := sess.Insert(item); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Update dashboard HasACL flag
|
||||
dashboard := dashboards.Dashboard{HasACL: true}
|
||||
_, err = sess.Cols("has_acl").Where("id=?", dashboardID).Update(&dashboard)
|
||||
return err
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func TestMetricsUsage(t *testing.T) {
|
||||
ss := db.InitTestDB(t)
|
||||
userStore := ProvideStore(ss, setting.NewCfg())
|
||||
@ -1029,91 +963,6 @@ func TestMetricsUsage(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// This function was copied from pkg/services/dashboards/database to circumvent
|
||||
// import cycles. When this org-related code is refactored into a service the
|
||||
// tests can the real GetDashboardACLInfoList functions
|
||||
func (ss *sqlStore) getDashboardACLInfoList(query *dashboards.GetDashboardACLInfoListQuery) ([]*dashboards.DashboardACLInfoDTO, error) {
|
||||
queryResult := make([]*dashboards.DashboardACLInfoDTO, 0)
|
||||
outerErr := ss.db.WithDbSession(context.Background(), func(dbSession *db.Session) error {
|
||||
falseStr := ss.dialect.BooleanStr(false)
|
||||
|
||||
if query.DashboardID == 0 {
|
||||
sql := `SELECT
|
||||
da.id,
|
||||
da.org_id,
|
||||
da.dashboard_id,
|
||||
da.user_id,
|
||||
da.team_id,
|
||||
da.permission,
|
||||
da.role,
|
||||
da.created,
|
||||
da.updated,
|
||||
'' as user_login,
|
||||
'' as user_email,
|
||||
'' as team,
|
||||
'' as title,
|
||||
'' as slug,
|
||||
'' as uid,` +
|
||||
falseStr + ` AS is_folder,` +
|
||||
falseStr + ` AS inherited
|
||||
FROM dashboard_acl as da
|
||||
WHERE da.dashboard_id = -1`
|
||||
return dbSession.SQL(sql).Find(&queryResult)
|
||||
}
|
||||
|
||||
rawSQL := `
|
||||
-- get permissions for the dashboard and its parent folder
|
||||
SELECT
|
||||
da.id,
|
||||
da.org_id,
|
||||
da.dashboard_id,
|
||||
da.user_id,
|
||||
da.team_id,
|
||||
da.permission,
|
||||
da.role,
|
||||
da.created,
|
||||
da.updated,
|
||||
u.login AS user_login,
|
||||
u.email AS user_email,
|
||||
ug.name AS team,
|
||||
ug.email AS team_email,
|
||||
d.title,
|
||||
d.slug,
|
||||
d.uid,
|
||||
d.is_folder,
|
||||
CASE WHEN (da.dashboard_id = -1 AND d.folder_id > 0) OR da.dashboard_id = d.folder_id THEN ` + ss.dialect.BooleanStr(true) + ` ELSE ` + falseStr + ` END AS inherited
|
||||
FROM dashboard as d
|
||||
LEFT JOIN dashboard folder on folder.id = d.folder_id
|
||||
LEFT JOIN dashboard_acl AS da ON
|
||||
da.dashboard_id = d.id OR
|
||||
da.dashboard_id = d.folder_id OR
|
||||
(
|
||||
-- include default permissions -->
|
||||
da.org_id = -1 AND (
|
||||
(folder.id IS NOT NULL AND folder.has_acl = ` + falseStr + `) OR
|
||||
(folder.id IS NULL AND d.has_acl = ` + falseStr + `)
|
||||
)
|
||||
)
|
||||
LEFT JOIN ` + ss.dialect.Quote("user") + ` AS u ON u.id = da.user_id
|
||||
LEFT JOIN team ug on ug.id = da.team_id
|
||||
WHERE d.org_id = ? AND d.id = ? AND da.id IS NOT NULL
|
||||
ORDER BY da.id ASC
|
||||
`
|
||||
|
||||
return dbSession.SQL(rawSQL, query.OrgID, query.DashboardID).Find(&queryResult)
|
||||
})
|
||||
|
||||
if outerErr != nil {
|
||||
return nil, outerErr
|
||||
}
|
||||
|
||||
for _, p := range queryResult {
|
||||
p.PermissionName = p.Permission.String()
|
||||
}
|
||||
|
||||
return queryResult, nil
|
||||
}
|
||||
|
||||
func createOrgAndUserSvc(t *testing.T, store db.DB, cfg *setting.Cfg) (org.Service, user.Service) {
|
||||
t.Helper()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user