package permissions_test import ( "context" "strconv" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/grafana/grafana/pkg/components/simplejson" "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/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore/permissions" "github.com/grafana/grafana/pkg/services/sqlstore/searchstore" "github.com/grafana/grafana/pkg/services/user" ) func TestIntegration_DashboardPermissionFilter(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } type testCase struct { desc string queryType string permission dashboards.PermissionType permissions []accesscontrol.Permission expectedResult int } tests := []testCase{ { desc: "Should be able to view all dashboards with wildcard scope", permission: dashboards.PERMISSION_VIEW, permissions: []accesscontrol.Permission{ {Action: dashboards.ActionDashboardsRead, Scope: dashboards.ScopeDashboardsAll}, }, expectedResult: 100, }, { desc: "Should be able to view all dashboards with folder wildcard scope", permission: dashboards.PERMISSION_VIEW, permissions: []accesscontrol.Permission{ {Action: dashboards.ActionDashboardsRead, Scope: dashboards.ScopeFoldersAll}, }, expectedResult: 100, }, { desc: "Should be able to view a subset of dashboards with dashboard scopes", permission: dashboards.PERMISSION_VIEW, permissions: []accesscontrol.Permission{ {Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:110"}, {Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:40"}, {Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:22"}, {Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:13"}, {Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:55"}, {Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:99"}, }, expectedResult: 6, }, { desc: "Should be able to view a subset of dashboards with dashboard action and folder scope", permission: dashboards.PERMISSION_VIEW, permissions: []accesscontrol.Permission{ {Action: dashboards.ActionDashboardsRead, Scope: "folders:uid:8"}, {Action: dashboards.ActionDashboardsRead, Scope: "folders:uid:10"}, }, expectedResult: 20, }, { desc: "Should be able to view all folders with folder wildcard", permission: dashboards.PERMISSION_VIEW, permissions: []accesscontrol.Permission{ {Action: dashboards.ActionFoldersRead, Scope: "folders:uid:*"}, }, expectedResult: 10, }, { desc: "Should be able to view a subset folders", permission: dashboards.PERMISSION_VIEW, permissions: []accesscontrol.Permission{ {Action: dashboards.ActionFoldersRead, Scope: "folders:uid:3"}, {Action: dashboards.ActionFoldersRead, Scope: "folders:uid:6"}, {Action: dashboards.ActionFoldersRead, Scope: "folders:uid:9"}, }, expectedResult: 3, }, { desc: "Should return folders and dashboard with 'edit' permission", permission: dashboards.PERMISSION_EDIT, permissions: []accesscontrol.Permission{ {Action: dashboards.ActionFoldersRead, Scope: "folders:uid:3"}, {Action: dashboards.ActionDashboardsCreate, Scope: "folders:uid:3"}, {Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:33"}, {Action: dashboards.ActionDashboardsWrite, Scope: "dashboards:uid:33"}, }, expectedResult: 2, }, { desc: "Should return folders that users can read alerts from", permission: dashboards.PERMISSION_VIEW, queryType: searchstore.TypeAlertFolder, permissions: []accesscontrol.Permission{ {Action: dashboards.ActionFoldersRead, Scope: "folders:uid:3"}, {Action: accesscontrol.ActionAlertingRuleRead, Scope: "folders:uid:3"}, {Action: dashboards.ActionFoldersRead, Scope: "folders:uid:8"}, {Action: accesscontrol.ActionAlertingRuleRead, Scope: "folders:uid:8"}, }, expectedResult: 2, }, { desc: "Should return folders that users can read alerts when user has read wildcard", permission: dashboards.PERMISSION_VIEW, queryType: searchstore.TypeAlertFolder, permissions: []accesscontrol.Permission{ {Action: dashboards.ActionFoldersRead, Scope: "*"}, {Action: accesscontrol.ActionAlertingRuleRead, Scope: "folders:uid:3"}, {Action: accesscontrol.ActionAlertingRuleRead, Scope: "folders:uid:8"}, }, expectedResult: 2, }, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { store := setupTest(t, 10, 100, tt.permissions) usr := &user.SignedInUser{OrgID: 1, OrgRole: org.RoleViewer, Permissions: map[int64]map[string][]string{1: accesscontrol.GroupScopesByAction(tt.permissions)}} filter := permissions.NewAccessControlDashboardPermissionFilter(usr, tt.permission, tt.queryType) var result int err := store.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { q, params := filter.Where() _, err := sess.SQL("SELECT COUNT(*) FROM dashboard WHERE "+q, params...).Get(&result) return err }) require.NoError(t, err) assert.Equal(t, tt.expectedResult, result) }) } } func setupTest(t *testing.T, numFolders, numDashboards int, permissions []accesscontrol.Permission) db.DB { store := db.InitTestDB(t) err := store.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error { dashes := make([]dashboards.Dashboard, 0, numFolders+numDashboards) for i := 1; i <= numFolders; i++ { str := strconv.Itoa(i) dashes = append(dashes, dashboards.Dashboard{ OrgID: 1, Slug: str, UID: str, Title: str, IsFolder: true, Data: simplejson.New(), Created: time.Now(), Updated: time.Now(), }) } // Seed 100 dashboard for i := numFolders + 1; i <= numFolders+numDashboards; i++ { str := strconv.Itoa(i) folderID := numFolders if i%numFolders != 0 { folderID = i % numFolders } dashes = append(dashes, dashboards.Dashboard{ OrgID: 1, IsFolder: false, FolderID: int64(folderID), UID: str, Slug: str, Title: str, Data: simplejson.New(), Created: time.Now(), Updated: time.Now(), }) } _, err := sess.InsertMulti(&dashes) if err != nil { return err } role := &accesscontrol.Role{ OrgID: 0, UID: "basic_viewer", Name: "basic:viewer", Updated: time.Now(), Created: time.Now(), } _, err = sess.Insert(role) if err != nil { return err } _, err = sess.Insert(accesscontrol.BuiltinRole{ OrgID: 0, RoleID: role.ID, Role: "Viewer", Created: time.Now(), Updated: time.Now(), }) if err != nil { return err } for i := range permissions { permissions[i].RoleID = role.ID permissions[i].Created = time.Now() permissions[i].Updated = time.Now() } if len(permissions) > 0 { _, err = sess.InsertMulti(&permissions) if err != nil { return err } } return nil }) require.NoError(t, err) return store }