package database import ( "context" "testing" "github.com/stretchr/testify/require" "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/models" "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/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 *DashboardStore setup := func(t *testing.T) { sqlStore = db.InitTestDB(t) quotaService := quotatest.New(false, nil) var err error dashboardStore, err = ProvideDashboardStore(sqlStore, sqlStore.Cfg, testFeatureToggles, tagimpl.ProvideService(sqlStore, sqlStore.Cfg), 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, false, "prod", "webapp") } t.Run("Dashboard permission with userId and teamId set to 0", func(t *testing.T) { setup(t) err := updateDashboardACL(t, dashboardStore, savedFolder.ID, dashboards.DashboardACL{ OrgID: 1, DashboardID: savedFolder.ID, Permission: models.PERMISSION_EDIT, }) require.Equal(t, models.ErrDashboardACLInfoMissing, err) }) t.Run("Folder acl should include default acl", func(t *testing.T) { setup(t) query := dashboards.GetDashboardACLInfoListQuery{DashboardID: savedFolder.ID, OrgID: 1} 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) { setup(t) query := dashboards.GetDashboardACLInfoListQuery{DashboardID: childDash.ID, OrgID: 1} 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) { setup(t) err := dashboardStore.UpdateDashboardACL(context.Background(), savedFolder.ID, nil) require.Nil(t, err) query := dashboards.GetDashboardACLInfoListQuery{DashboardID: childDash.ID, OrgID: 1} 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) { setup(t) err := updateDashboardACL(t, dashboardStore, savedFolder.ID, dashboards.DashboardACL{ OrgID: 1, UserID: currentUser.ID, DashboardID: savedFolder.ID, Permission: models.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: 1} 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: 1, UserID: currentUser.ID, DashboardID: childDash.ID, Permission: models.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: 1, 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) { setup(t) err := updateDashboardACL(t, dashboardStore, childDash.ID, dashboards.DashboardACL{ OrgID: 1, UserID: currentUser.ID, DashboardID: childDash.ID, Permission: models.PERMISSION_EDIT, }) require.Nil(t, err) query := dashboards.GetDashboardACLInfoListQuery{OrgID: 1, 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) { setup(t) err := updateDashboardACL(t, dashboardStore, savedFolder.ID, dashboards.DashboardACL{ OrgID: 1, UserID: currentUser.ID, DashboardID: savedFolder.ID, Permission: models.PERMISSION_EDIT, }) require.Nil(t, err) q1 := &dashboards.GetDashboardACLInfoListQuery{DashboardID: savedFolder.ID, OrgID: 1} q1Result, err := dashboardStore.GetDashboardACLInfoList(context.Background(), q1) require.Nil(t, err) require.Equal(t, savedFolder.ID, q1Result[0].DashboardID) require.Equal(t, models.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: 1} 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) { 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: models.PERMISSION_EDIT, }) require.Nil(t, err) q1 := &dashboards.GetDashboardACLInfoListQuery{DashboardID: savedFolder.ID, OrgID: 1} q1Result, err := dashboardStore.GetDashboardACLInfoList(context.Background(), q1) require.Nil(t, err) require.Equal(t, savedFolder.ID, q1Result[0].DashboardID) require.Equal(t, models.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) { 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: models.PERMISSION_ADMIN, }) require.Nil(t, err) q3 := &dashboards.GetDashboardACLInfoListQuery{DashboardID: savedFolder.ID, OrgID: 1} 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, models.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) { setup(t) var rootFolderId int64 = 0 //sqlStore := db.InitTestDB(t) query := dashboards.GetDashboardACLInfoListQuery{DashboardID: rootFolderId, OrgID: 1} 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) require.NoError(t, err) currentUserCmd := user.CreateUserCommand{Login: name, Email: name + "@test.com", Name: "a " + name, IsAdmin: isAdmin} currentUser, err := usrSvc.CreateUserForTests(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) return *currentUser }