PublicDashboards: Audit table pagination (#69823)

This commit is contained in:
Juan Cabanas
2023-06-21 10:48:09 -03:00
committed by GitHub
parent ed5a697825
commit ee73d41d24
20 changed files with 475 additions and 305 deletions
+1 -1
View File
@@ -167,7 +167,7 @@ func (ss *sqlStore) HandleAlertsQuery(ctx context.Context, query *alertmodels.Ge
builder.Write(")")
}
builder.WriteDashboardPermissionFilter(query.User, dashboards.PERMISSION_VIEW)
builder.WriteDashboardPermissionFilter(query.User, dashboards.PERMISSION_VIEW, "")
builder.Write(" ORDER BY name ASC")
+3 -3
View File
@@ -250,7 +250,7 @@ func getLibraryElements(c context.Context, store db.DB, cfg *setting.Cfg, signed
builder.Write(getFromLibraryElementDTOWithMeta(store.GetDialect()))
builder.Write(" INNER JOIN dashboard AS dashboard on le.folder_id = dashboard.id AND le.folder_id <> 0")
writeParamSelectorSQL(&builder, params...)
builder.WriteDashboardPermissionFilter(signedInUser, dashboards.PERMISSION_VIEW)
builder.WriteDashboardPermissionFilter(signedInUser, dashboards.PERMISSION_VIEW, "")
builder.Write(` OR dashboard.id=0`)
if err := session.SQL(builder.GetSQLString(), builder.GetParams()...).Find(&libraryElements); err != nil {
return err
@@ -372,7 +372,7 @@ func (l *LibraryElementService) getAllLibraryElements(c context.Context, signedI
return err
}
if signedInUser.OrgRole != org.RoleAdmin {
builder.WriteDashboardPermissionFilter(signedInUser, dashboards.PERMISSION_VIEW)
builder.WriteDashboardPermissionFilter(signedInUser, dashboards.PERMISSION_VIEW, "")
}
if query.SortDirection == search.SortAlphaDesc.Name {
builder.Write(" ORDER BY 1 DESC")
@@ -603,7 +603,7 @@ func (l *LibraryElementService) getConnections(c context.Context, signedInUser *
builder.Write(" INNER JOIN dashboard AS dashboard on lec.connection_id = dashboard.id")
builder.Write(` WHERE lec.element_id=?`, element.ID)
if signedInUser.OrgRole != org.RoleAdmin {
builder.WriteDashboardPermissionFilter(signedInUser, dashboards.PERMISSION_VIEW)
builder.WriteDashboardPermissionFilter(signedInUser, dashboards.PERMISSION_VIEW, "")
}
if err := session.SQL(builder.GetSQLString(), builder.GetParams()...).Find(&libraryElementConnections); err != nil {
return err
+18 -1
View File
@@ -90,7 +90,24 @@ func (api *Api) RegisterAPIEndpoints() {
// ListPublicDashboards Gets list of public dashboards by orgId
// GET /api/dashboards/public-dashboards
func (api *Api) ListPublicDashboards(c *contextmodel.ReqContext) response.Response {
resp, err := api.PublicDashboardService.FindAll(c.Req.Context(), c.SignedInUser, c.OrgID)
perPage := c.QueryInt("perpage")
if perPage <= 0 {
perPage = 1000
}
page := c.QueryInt("page")
if page < 1 {
page = 1
}
resp, err := api.PublicDashboardService.FindAllWithPagination(c.Req.Context(), &PublicDashboardListQuery{
OrgID: c.OrgID,
Query: c.Query("query"),
Page: page,
Limit: perPage,
User: c.SignedInUser,
})
if err != nil {
return response.Err(err)
}
+13 -11
View File
@@ -89,19 +89,21 @@ func TestAPIFeatureFlag(t *testing.T) {
}
func TestAPIListPublicDashboard(t *testing.T) {
successResp := []PublicDashboardListResponse{
{
Uid: "1234asdfasdf",
AccessToken: "asdfasdf",
DashboardUid: "abc1234",
IsEnabled: true,
successResp := &PublicDashboardListResponseWithPagination{
PublicDashboards: []*PublicDashboardListResponse{
{
Uid: "1234asdfasdf",
AccessToken: "asdfasdf",
DashboardUid: "abc1234",
IsEnabled: true,
},
},
}
testCases := []struct {
Name string
User *user.SignedInUser
Response []PublicDashboardListResponse
Response *PublicDashboardListResponseWithPagination
ResponseErr error
ExpectedHttpResponse int
}{
@@ -131,7 +133,7 @@ func TestAPIListPublicDashboard(t *testing.T) {
for _, test := range testCases {
t.Run(test.Name, func(t *testing.T) {
service := publicdashboards.NewFakePublicDashboardService(t)
service.On("FindAll", mock.Anything, mock.Anything, mock.Anything).
service.On("FindAllWithPagination", mock.Anything, mock.Anything, mock.Anything).
Return(test.Response, test.ResponseErr).Maybe()
cfg := setting.NewCfg()
@@ -143,10 +145,10 @@ func TestAPIListPublicDashboard(t *testing.T) {
assert.Equal(t, test.ExpectedHttpResponse, response.Code)
if test.ExpectedHttpResponse == http.StatusOK {
var jsonResp []PublicDashboardListResponse
var jsonResp PublicDashboardListResponseWithPagination
err := json.Unmarshal(response.Body.Bytes(), &jsonResp)
require.NoError(t, err)
assert.Equal(t, jsonResp[0].Uid, "1234asdfasdf")
assert.Equal(t, jsonResp.PublicDashboards[0].Uid, "1234asdfasdf")
}
if test.ResponseErr != nil {
@@ -155,7 +157,7 @@ func TestAPIListPublicDashboard(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, "Internal server error", errResp.Message)
assert.Equal(t, "publicdashboards.internalServerError", errResp.MessageID)
service.AssertNotCalled(t, "FindAll")
service.AssertNotCalled(t, "FindAllWithPagination")
}
})
}
@@ -322,7 +322,7 @@ func TestIntegrationUnauthenticatedUserCanGetPubdashPanelQueryData(t *testing.T)
annotationsService := annotationstest.NewFakeAnnotationsRepo()
// create public dashboard
store := publicdashboardsStore.ProvideStore(db)
store := publicdashboardsStore.ProvideStore(db, db.Cfg, featuremgmt.WithFeatures())
cfg := setting.NewCfg()
ac := acmock.New()
ws := publicdashboardsService.ProvideServiceWrapper(store)
@@ -7,9 +7,13 @@ import (
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/services/publicdashboards"
. "github.com/grafana/grafana/pkg/services/publicdashboards/models"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
"github.com/grafana/grafana/pkg/setting"
)
// Define the storage implementation. We're generating the mock implementation
@@ -17,6 +21,8 @@ import (
type PublicDashboardStoreImpl struct {
sqlStore db.DB
log log.Logger
cfg *setting.Cfg
features featuremgmt.FeatureToggles
}
var LogPrefix = "publicdashboards.store"
@@ -26,25 +32,54 @@ var LogPrefix = "publicdashboards.store"
var _ publicdashboards.Store = (*PublicDashboardStoreImpl)(nil)
// Factory used by wire to dependency injection
func ProvideStore(sqlStore db.DB) *PublicDashboardStoreImpl {
func ProvideStore(sqlStore db.DB, cfg *setting.Cfg, features featuremgmt.FeatureToggles) *PublicDashboardStoreImpl {
return &PublicDashboardStoreImpl{
sqlStore: sqlStore,
log: log.New(LogPrefix),
cfg: cfg,
features: features,
}
}
// FindAll Returns a list of public dashboards by orgId
func (d *PublicDashboardStoreImpl) FindAll(ctx context.Context, orgId int64) ([]PublicDashboardListResponse, error) {
resp := make([]PublicDashboardListResponse, 0)
// FindAllWithPagination Returns a list of public dashboards by orgId, based on permissions and with pagination
func (d *PublicDashboardStoreImpl) FindAllWithPagination(ctx context.Context, query *PublicDashboardListQuery) (*PublicDashboardListResponseWithPagination, error) {
resp := &PublicDashboardListResponseWithPagination{
PublicDashboards: make([]*PublicDashboardListResponse, 0),
TotalCount: 0,
}
err := d.sqlStore.WithDbSession(ctx, func(sess *db.Session) error {
sess.Table("dashboard_public").Select(
"dashboard_public.uid, dashboard_public.access_token, dashboard.uid as dashboard_uid, dashboard_public.is_enabled, dashboard.title").
Join("LEFT", "dashboard", "dashboard.uid = dashboard_public.dashboard_uid AND dashboard.org_id = dashboard_public.org_id").
Where("dashboard_public.org_id = ?", orgId).
OrderBy(" dashboard.title IS NULL, dashboard.title ASC")
recursiveQueriesAreSupported, err := d.sqlStore.RecursiveQueriesAreSupported()
if err != nil {
return nil, err
}
err := sess.Find(&resp)
pubdashBuilder := db.NewSqlBuilder(d.cfg, d.features, d.sqlStore.GetDialect(), recursiveQueriesAreSupported)
pubdashBuilder.Write("SELECT dashboard_public.uid, dashboard_public.access_token, dashboard.uid as dashboard_uid, dashboard_public.is_enabled, dashboard.title")
pubdashBuilder.Write(" FROM dashboard_public")
pubdashBuilder.Write(" JOIN dashboard ON dashboard.uid = dashboard_public.dashboard_uid AND dashboard.org_id = dashboard_public.org_id")
pubdashBuilder.Write(` WHERE dashboard_public.org_id = ?`, query.OrgID)
if query.User.OrgRole != org.RoleAdmin {
pubdashBuilder.WriteDashboardPermissionFilter(query.User, dashboards.PERMISSION_VIEW, searchstore.TypeDashboard)
}
pubdashBuilder.Write(" ORDER BY dashboard.title")
pubdashBuilder.Write(d.sqlStore.GetDialect().LimitOffset(int64(query.Limit), int64(query.Offset)))
counterBuilder := db.NewSqlBuilder(d.cfg, d.features, d.sqlStore.GetDialect(), recursiveQueriesAreSupported)
counterBuilder.Write("SELECT COUNT(*)")
counterBuilder.Write(" FROM dashboard_public")
counterBuilder.Write(" JOIN dashboard ON dashboard.uid = dashboard_public.dashboard_uid AND dashboard.org_id = dashboard_public.org_id")
counterBuilder.Write(` WHERE dashboard_public.org_id = ?`, query.OrgID)
if query.User.OrgRole != org.RoleAdmin {
counterBuilder.WriteDashboardPermissionFilter(query.User, dashboards.PERMISSION_VIEW, searchstore.TypeDashboard)
}
err = d.sqlStore.WithDbSession(ctx, func(sess *db.Session) error {
err := sess.SQL(pubdashBuilder.GetSQLString(), pubdashBuilder.GetParams()...).Find(&resp.PublicDashboards)
if err != nil {
return err
}
_, err = sess.SQL(counterBuilder.GetSQLString(), counterBuilder.GetParams()...).Get(&resp.TotalCount)
return err
})
@@ -2,18 +2,24 @@ package database
import (
"context"
"fmt"
"strings"
"testing"
"time"
"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"
dashboardsDB "github.com/grafana/grafana/pkg/services/dashboards/database"
"github.com/grafana/grafana/pkg/services/featuremgmt"
. "github.com/grafana/grafana/pkg/services/publicdashboards/models"
"github.com/grafana/grafana/pkg/services/publicdashboards/service"
"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/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
"github.com/stretchr/testify/assert"
@@ -34,38 +40,122 @@ func TestIntegrationListPublicDashboard(t *testing.T) {
if testing.Short() {
t.Skip("skipping integration test")
}
sqlStore, cfg := db.InitTestDBwithCfg(t, db.InitTestDBOpt{FeatureFlags: []string{featuremgmt.FlagPublicDashboards}})
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := ProvideStore(sqlStore)
var sqlStore *sqlstore.SQLStore
var cfg *setting.Cfg
var aDash *dashboards.Dashboard
var bDash *dashboards.Dashboard
var cDash *dashboards.Dashboard
var aPublicDash *PublicDashboard
var bPublicDash *PublicDashboard
var cPublicDash *PublicDashboard
var orgId int64 = 1
bDash := insertTestDashboard(t, dashboardStore, "b", orgId, 0, true)
aDash := insertTestDashboard(t, dashboardStore, "a", orgId, 0, true)
cDash := insertTestDashboard(t, dashboardStore, "c", orgId, 0, true)
var publicdashboardStore *PublicDashboardStoreImpl
// these are in order of how they should be returned from ListPUblicDashboards
a := insertPublicDashboard(t, publicdashboardStore, aDash.UID, orgId, false, PublicShareType)
b := insertPublicDashboard(t, publicdashboardStore, bDash.UID, orgId, true, PublicShareType)
c := insertPublicDashboard(t, publicdashboardStore, cDash.UID, orgId, true, PublicShareType)
setup := func() {
sqlStore, cfg = db.InitTestDBwithCfg(t, db.InitTestDBOpt{FeatureFlags: []string{featuremgmt.FlagPublicDashboards}})
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
publicdashboardStore = ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
// this is case that can happen as of now, however, postgres and mysql sort
// null in the exact opposite fashion and there is no shared syntax to sort
// nulls in the same way in all 3 db's.
//d := insertPublicDashboard(t, publicdashboardStore, "missing", orgId, false)
bDash = insertTestDashboard(t, dashboardStore, "b", orgId, 0, false)
aDash = insertTestDashboard(t, dashboardStore, "a", orgId, 0, false)
cDash = insertTestDashboard(t, dashboardStore, "c", orgId, 0, false)
// should not be included in response
_ = insertPublicDashboard(t, publicdashboardStore, "wrongOrgId", 777, false, PublicShareType)
// these are in order of how they should be returned from ListPUblicDashboards
aPublicDash = insertPublicDashboard(t, publicdashboardStore, aDash.UID, orgId, false, PublicShareType)
bPublicDash = insertPublicDashboard(t, publicdashboardStore, bDash.UID, orgId, true, PublicShareType)
cPublicDash = insertPublicDashboard(t, publicdashboardStore, cDash.UID, orgId, true, PublicShareType)
}
resp, err := publicdashboardStore.FindAll(context.Background(), orgId)
require.NoError(t, err)
t.Run("FindAllWithPagination will return dashboard list based on orgId with pagination", func(t *testing.T) {
setup()
assert.Len(t, resp, 3)
assert.Equal(t, resp[0].Uid, a.Uid)
assert.Equal(t, resp[1].Uid, b.Uid)
assert.Equal(t, resp[2].Uid, c.Uid)
// should not be included in response
_ = insertPublicDashboard(t, publicdashboardStore, "wrongOrgId", 777, false, PublicShareType)
permissions := []accesscontrol.Permission{
{Action: dashboards.ActionDashboardsRead, Scope: fmt.Sprintf("dashboards:uid:%s", aDash.UID)},
{Action: dashboards.ActionDashboardsRead, Scope: fmt.Sprintf("dashboards:uid:%s", bDash.UID)},
{Action: dashboards.ActionDashboardsRead, Scope: fmt.Sprintf("dashboards:uid:%s", cDash.UID)},
}
err := insertPermissions(sqlStore, orgId, "viewer", permissions)
require.NoError(t, err)
query := &PublicDashboardListQuery{
User: &user.SignedInUser{UserID: 1, OrgID: orgId, Permissions: map[int64]map[string][]string{orgId: accesscontrol.GroupScopesByAction(permissions)}},
OrgID: orgId,
Page: 1,
Limit: 50,
Offset: 0,
}
resp, err := publicdashboardStore.FindAllWithPagination(context.Background(), query)
require.NoError(t, err)
assert.Len(t, resp.PublicDashboards, 3)
assert.Equal(t, resp.PublicDashboards[0].Uid, aPublicDash.Uid)
assert.Equal(t, resp.PublicDashboards[1].Uid, bPublicDash.Uid)
assert.Equal(t, resp.PublicDashboards[2].Uid, cPublicDash.Uid)
assert.Equal(t, resp.TotalCount, int64(3))
})
t.Run("FindAllWithPagination will return dashboard list based on read permissions with pagination", func(t *testing.T) {
setup()
permissions := []accesscontrol.Permission{
{Action: dashboards.ActionDashboardsRead, Scope: fmt.Sprintf("dashboards:uid:%s", aDash.UID)},
{Action: dashboards.ActionDashboardsRead, Scope: fmt.Sprintf("dashboards:uid:%s", cDash.UID)},
}
err := insertPermissions(sqlStore, orgId, "viewer", permissions)
require.NoError(t, err)
query := &PublicDashboardListQuery{
User: &user.SignedInUser{UserID: 1, OrgID: orgId, Permissions: map[int64]map[string][]string{orgId: accesscontrol.GroupScopesByAction(permissions)}},
OrgID: orgId,
Page: 1,
Limit: 50,
Offset: 0,
}
resp, err := publicdashboardStore.FindAllWithPagination(context.Background(), query)
require.NoError(t, err)
assert.Len(t, resp.PublicDashboards, 2)
assert.Equal(t, resp.PublicDashboards[0].Uid, aPublicDash.Uid)
assert.Equal(t, resp.PublicDashboards[1].Uid, cPublicDash.Uid)
assert.Equal(t, resp.TotalCount, int64(2))
})
t.Run("FindAllWithPagination will return empty dashboard list based on read permissions with pagination", func(t *testing.T) {
setup()
permissions := []accesscontrol.Permission{
{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:another-dashboard-uid"},
{Action: dashboards.ActionDashboardsRead, Scope: "dashboards:uid:another-dashboard-2-uid"},
}
err := insertPermissions(sqlStore, orgId, "viewer", permissions)
require.NoError(t, err)
query := &PublicDashboardListQuery{
User: &user.SignedInUser{UserID: 1, OrgID: orgId, Permissions: map[int64]map[string][]string{orgId: accesscontrol.GroupScopesByAction(permissions)}},
OrgID: orgId,
Page: 1,
Limit: 50,
Offset: 0,
}
resp, err := publicdashboardStore.FindAllWithPagination(context.Background(), query)
require.NoError(t, err)
assert.Len(t, resp.PublicDashboards, 0)
assert.Equal(t, resp.TotalCount, int64(0))
})
}
func TestIntegrationFindDashboard(t *testing.T) {
@@ -84,7 +174,7 @@ func TestIntegrationFindDashboard(t *testing.T) {
store, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
dashboardStore = store
publicdashboardStore = ProvideStore(sqlStore)
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
}
@@ -114,7 +204,7 @@ func TestIntegrationExistsEnabledByAccessToken(t *testing.T) {
store, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
dashboardStore = store
publicdashboardStore = ProvideStore(sqlStore)
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
}
t.Run("ExistsEnabledByAccessToken will return true when at least one public dashboard has a matching access token", func(t *testing.T) {
@@ -187,7 +277,7 @@ func TestIntegrationExistsEnabledByDashboardUid(t *testing.T) {
store, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
dashboardStore = store
publicdashboardStore = ProvideStore(sqlStore)
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
}
@@ -252,7 +342,7 @@ func TestIntegrationFindByDashboardUid(t *testing.T) {
store, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
dashboardStore = store
publicdashboardStore = ProvideStore(sqlStore)
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
}
@@ -319,7 +409,7 @@ func TestIntegrationFindByAccessToken(t *testing.T) {
sqlStore, cfg = db.InitTestDBwithCfg(t)
dashboardStore, err = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotatest.New(false, nil))
require.NoError(t, err)
publicdashboardStore = ProvideStore(sqlStore)
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
}
@@ -389,7 +479,7 @@ func TestIntegrationCreatePublicDashboard(t *testing.T) {
store, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
dashboardStore = store
publicdashboardStore = ProvideStore(sqlStore)
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
savedDashboard2 = insertTestDashboard(t, dashboardStore, "testDashie2", 1, 0, true)
insertPublicDashboard(t, publicdashboardStore, savedDashboard2.UID, savedDashboard2.OrgID, false, PublicShareType)
@@ -468,7 +558,7 @@ func TestIntegrationUpdatePublicDashboard(t *testing.T) {
quotaService := quotatest.New(false, nil)
dashboardStore, err = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
publicdashboardStore = ProvideStore(sqlStore)
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
anotherSavedDashboard = insertTestDashboard(t, dashboardStore, "test another Dashie", 1, 0, true)
}
@@ -572,7 +662,7 @@ func TestIntegrationGetOrgIdByAccessToken(t *testing.T) {
quotaService := quotatest.New(false, nil)
dashboardStore, err = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
publicdashboardStore = ProvideStore(sqlStore)
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
}
t.Run("GetOrgIdByAccessToken will OrgId when enabled", func(t *testing.T) {
@@ -644,7 +734,7 @@ func TestIntegrationDelete(t *testing.T) {
sqlStore, cfg = db.InitTestDBwithCfg(t)
dashboardStore, err = dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotatest.New(false, nil))
require.NoError(t, err)
publicdashboardStore = ProvideStore(sqlStore)
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true)
savedPublicDashboard = insertPublicDashboard(t, publicdashboardStore, savedDashboard.UID, savedDashboard.OrgID, true, PublicShareType)
}
@@ -675,7 +765,7 @@ func TestGetDashboardByFolder(t *testing.T) {
t.Run("returns nil when dashboard is not a folder", func(t *testing.T) {
sqlStore, _ := db.InitTestDBwithCfg(t)
dashboard := &dashboards.Dashboard{IsFolder: false}
store := ProvideStore(sqlStore)
store := ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
pubdashes, err := store.FindByDashboardFolder(context.Background(), dashboard)
require.NoError(t, err)
@@ -684,7 +774,7 @@ func TestGetDashboardByFolder(t *testing.T) {
t.Run("returns nil when dashboard is nil", func(t *testing.T) {
sqlStore, _ := db.InitTestDBwithCfg(t)
store := ProvideStore(sqlStore)
store := ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
pubdashes, err := store.FindByDashboardFolder(context.Background(), nil)
require.NoError(t, err)
@@ -696,7 +786,7 @@ func TestGetDashboardByFolder(t *testing.T) {
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
pubdashStore := ProvideStore(sqlStore)
pubdashStore := ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
dashboard := insertTestDashboard(t, dashboardStore, "title", 1, 1, true, PublicShareType)
pubdash := insertPublicDashboard(t, pubdashStore, dashboard.UID, dashboard.OrgID, true, PublicShareType)
dashboard2 := insertTestDashboard(t, dashboardStore, "title", 1, 2, true, PublicShareType)
@@ -729,7 +819,7 @@ func TestGetMetrics(t *testing.T) {
store, err := dashboardsDB.ProvideDashboardStore(sqlStore, cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, cfg), quotaService)
require.NoError(t, err)
dashboardStore = store
publicdashboardStore = ProvideStore(sqlStore)
publicdashboardStore = ProvideStore(sqlStore, cfg, featuremgmt.WithFeatures())
savedDashboard = insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, false)
savedDashboard2 = insertTestDashboard(t, dashboardStore, "testDashie2", 1, 0, false)
savedDashboard3 = insertTestDashboard(t, dashboardStore, "testDashie3", 2, 0, false)
@@ -827,3 +917,50 @@ func insertPublicDashboard(t *testing.T, publicdashboardStore *PublicDashboardSt
return pubdash
}
func insertPermissions(sqlStore *sqlstore.SQLStore, orgId int64, role string, permissions []accesscontrol.Permission) error {
return sqlStore.WithDbSession(context.Background(), func(sess *sqlstore.DBSession) error {
newRole := &accesscontrol.Role{
OrgID: orgId,
UID: fmt.Sprintf("basic_%s", role),
Name: fmt.Sprintf("basic:%s", role),
Updated: time.Now(),
Created: time.Now(),
}
_, err := sess.Insert(newRole)
if err != nil {
return err
}
_, err = sess.Insert(accesscontrol.BuiltinRole{
OrgID: orgId,
RoleID: newRole.ID,
Role: strings.ToUpper(role[:1]) + role[1:],
Created: time.Now(),
Updated: time.Now(),
})
if err != nil {
return err
}
for i := range permissions {
permissions[i].RoleID = newRole.ID
permissions[i].Created = time.Now()
permissions[i].Updated = time.Now()
}
_, err = sess.InsertMulti(&permissions)
if err != nil {
return err
}
_, err = sess.Insert(accesscontrol.UserRole{
OrgID: orgId,
RoleID: newRole.ID,
UserID: 1,
Created: time.Now(),
})
return err
})
}
@@ -5,6 +5,7 @@ import (
"time"
"github.com/grafana/grafana/pkg/kinds/dashboard"
"github.com/grafana/grafana/pkg/services/user"
)
// PublicDashboardErr represents a dashboard error.
@@ -103,6 +104,22 @@ func (pd PublicDashboard) TableName() string {
return "dashboard_public"
}
type PublicDashboardListQuery struct {
OrgID int64
Query string
Page int
Limit int
Offset int
User *user.SignedInUser
}
type PublicDashboardListResponseWithPagination struct {
PublicDashboards []*PublicDashboardListResponse `json:"publicDashboards"`
TotalCount int64 `json:"totalCount"`
Page int `json:"page"`
PerPage int `json:"perPage"`
}
type PublicDashboardListResponse struct {
Uid string `json:"uid" xorm:"uid"`
AccessToken string `json:"accessToken" xorm:"access_token"`
@@ -139,22 +139,22 @@ func (_m *FakePublicDashboardService) Find(ctx context.Context, uid string) (*mo
return r0, r1
}
// FindAll provides a mock function with given fields: ctx, u, orgId
func (_m *FakePublicDashboardService) FindAll(ctx context.Context, u *user.SignedInUser, orgId int64) ([]models.PublicDashboardListResponse, error) {
ret := _m.Called(ctx, u, orgId)
// FindAllWithPagination provides a mock function with given fields: ctx, query
func (_m *FakePublicDashboardService) FindAllWithPagination(ctx context.Context, query *models.PublicDashboardListQuery) (*models.PublicDashboardListResponseWithPagination, error) {
ret := _m.Called(ctx, query)
var r0 []models.PublicDashboardListResponse
if rf, ok := ret.Get(0).(func(context.Context, *user.SignedInUser, int64) []models.PublicDashboardListResponse); ok {
r0 = rf(ctx, u, orgId)
var r0 *models.PublicDashboardListResponseWithPagination
if rf, ok := ret.Get(0).(func(context.Context, *models.PublicDashboardListQuery) *models.PublicDashboardListResponseWithPagination); ok {
r0 = rf(ctx, query)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]models.PublicDashboardListResponse)
r0 = ret.Get(0).(*models.PublicDashboardListResponseWithPagination)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *user.SignedInUser, int64) error); ok {
r1 = rf(ctx, u, orgId)
if rf, ok := ret.Get(1).(func(context.Context, *models.PublicDashboardListQuery) error); ok {
r1 = rf(ctx, query)
} else {
r1 = ret.Error(1)
}
@@ -123,22 +123,22 @@ func (_m *FakePublicDashboardStore) Find(ctx context.Context, uid string) (*mode
return r0, r1
}
// FindAll provides a mock function with given fields: ctx, orgId
func (_m *FakePublicDashboardStore) FindAll(ctx context.Context, orgId int64) ([]models.PublicDashboardListResponse, error) {
ret := _m.Called(ctx, orgId)
// FindAllWithPagination provides a mock function with given fields: ctx, query
func (_m *FakePublicDashboardStore) FindAllWithPagination(ctx context.Context, query *models.PublicDashboardListQuery) (*models.PublicDashboardListResponseWithPagination, error) {
ret := _m.Called(ctx, query)
var r0 []models.PublicDashboardListResponse
if rf, ok := ret.Get(0).(func(context.Context, int64) []models.PublicDashboardListResponse); ok {
r0 = rf(ctx, orgId)
var r0 *models.PublicDashboardListResponseWithPagination
if rf, ok := ret.Get(0).(func(context.Context, *models.PublicDashboardListQuery) *models.PublicDashboardListResponseWithPagination); ok {
r0 = rf(ctx, query)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]models.PublicDashboardListResponse)
r0 = ret.Get(0).(*models.PublicDashboardListResponseWithPagination)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
r1 = rf(ctx, orgId)
if rf, ok := ret.Get(1).(func(context.Context, *models.PublicDashboardListQuery) error); ok {
r1 = rf(ctx, query)
} else {
r1 = ret.Error(1)
}
@@ -21,7 +21,7 @@ type Service interface {
FindByDashboardUid(ctx context.Context, orgId int64, dashboardUid string) (*PublicDashboard, error)
FindAnnotations(ctx context.Context, reqDTO AnnotationsQueryDTO, accessToken string) ([]AnnotationEvent, error)
FindDashboard(ctx context.Context, orgId int64, dashboardUid string) (*dashboards.Dashboard, error)
FindAll(ctx context.Context, u *user.SignedInUser, orgId int64) ([]PublicDashboardListResponse, error)
FindAllWithPagination(ctx context.Context, query *PublicDashboardListQuery) (*PublicDashboardListResponseWithPagination, error)
Find(ctx context.Context, uid string) (*PublicDashboard, error)
Create(ctx context.Context, u *user.SignedInUser, dto *SavePublicDashboardDTO) (*PublicDashboard, error)
Update(ctx context.Context, u *user.SignedInUser, dto *SavePublicDashboardDTO) (*PublicDashboard, error)
@@ -52,7 +52,7 @@ type Store interface {
FindByAccessToken(ctx context.Context, accessToken string) (*PublicDashboard, error)
FindByDashboardUid(ctx context.Context, orgId int64, dashboardUid string) (*PublicDashboard, error)
FindDashboard(ctx context.Context, orgId int64, dashboardUid string) (*dashboards.Dashboard, error)
FindAll(ctx context.Context, orgId int64) ([]PublicDashboardListResponse, error)
FindAllWithPagination(ctx context.Context, query *PublicDashboardListQuery) (*PublicDashboardListResponseWithPagination, error)
Create(ctx context.Context, cmd SavePublicDashboardCommand) (int64, error)
Update(ctx context.Context, cmd SavePublicDashboardCommand) (int64, error)
Delete(ctx context.Context, uid string) (int64, error)
@@ -663,7 +663,7 @@ func TestGetQueryDataResponse(t *testing.T) {
sqlStore := sqlstore.InitTestDB(t)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotatest.New(false, nil))
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
fakeQueryService := &query.FakeQueryService{}
fakeQueryService.On("QueryData", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(&backend.QueryDataResponse{}, nil)
@@ -1109,7 +1109,7 @@ func TestGetMetricRequest(t *testing.T) {
sqlStore := db.InitTestDB(t)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotatest.New(false, nil))
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
publicDashboard := &PublicDashboard{
Uid: "1",
@@ -1194,7 +1194,7 @@ func TestBuildMetricRequest(t *testing.T) {
sqlStore := db.InitTestDB(t)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotatest.New(false, nil))
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
publicDashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
nonPublicDashboard := insertTestDashboard(t, dashboardStore, "testNonPublicDashie", 1, 0, true, []map[string]interface{}{}, nil)
@@ -276,14 +276,18 @@ func (pd *PublicDashboardServiceImpl) NewPublicDashboardAccessToken(ctx context.
return "", ErrInternalServerError.Errorf("failed to generate a unique accessToken for public dashboard")
}
// FindAll Returns a list of public dashboards by orgId
func (pd *PublicDashboardServiceImpl) FindAll(ctx context.Context, u *user.SignedInUser, orgId int64) ([]PublicDashboardListResponse, error) {
publicDashboards, err := pd.store.FindAll(ctx, orgId)
// FindAllWithPagination Returns a list of public dashboards by orgId, based on permissions and with pagination
func (pd *PublicDashboardServiceImpl) FindAllWithPagination(ctx context.Context, query *PublicDashboardListQuery) (*PublicDashboardListResponseWithPagination, error) {
query.Offset = query.Limit * (query.Page - 1)
resp, err := pd.store.FindAllWithPagination(ctx, query)
if err != nil {
return nil, ErrInternalServerError.Errorf("FindAll: %w", err)
return nil, ErrInternalServerError.Errorf("FindAllWithPagination: %w", err)
}
return pd.filterDashboardsByPermissions(ctx, u, publicDashboards)
resp.Page = query.Page
resp.PerPage = query.Limit
return resp, nil
}
func (pd *PublicDashboardServiceImpl) ExistsEnabledByDashboardUid(ctx context.Context, dashboardUid string) (bool, error) {
@@ -371,25 +375,6 @@ func (pd *PublicDashboardServiceImpl) logIsEnabledChanged(existingPubdash *Publi
}
}
// Filter out dashboards that user does not have read access to
func (pd *PublicDashboardServiceImpl) filterDashboardsByPermissions(ctx context.Context, u *user.SignedInUser, publicDashboards []PublicDashboardListResponse) ([]PublicDashboardListResponse, error) {
result := make([]PublicDashboardListResponse, 0)
for i := range publicDashboards {
hasAccess, err := pd.ac.Evaluate(ctx, u, accesscontrol.EvalPermission(dashboards.ActionDashboardsRead, dashboards.ScopeDashboardsProvider.GetResourceScopeUID(publicDashboards[i].DashboardUid)))
// If original dashboard does not exist, the public dashboard is an orphan. We want to list it anyway
if err != nil && !errors.Is(err, dashboards.ErrDashboardNotFound) {
return nil, ErrInternalServerError.Errorf("filterDashboardsByPermissions: error evaluating permissions %w", err)
}
// If user has access to the original dashboard or the dashboard does not exist, add the pubdash to the result
if hasAccess || errors.Is(err, dashboards.ErrDashboardNotFound) {
result = append(result, publicDashboards[i])
}
}
return result, nil
}
// Checks to see if PublicDashboard.ExistsEnabledByDashboardUid is true on create or changed on update
func publicDashboardIsEnabledChanged(existingPubdash *PublicDashboard, newPubdash *PublicDashboard) bool {
// creating dashboard, enabled true
@@ -195,7 +195,7 @@ func TestCreatePublicDashboard(t *testing.T) {
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
@@ -285,7 +285,7 @@ func TestCreatePublicDashboard(t *testing.T) {
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
@@ -325,7 +325,7 @@ func TestCreatePublicDashboard(t *testing.T) {
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
@@ -359,7 +359,7 @@ func TestCreatePublicDashboard(t *testing.T) {
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
templateVars := make([]map[string]interface{}, 1)
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, templateVars, nil)
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
@@ -474,7 +474,7 @@ func TestCreatePublicDashboard(t *testing.T) {
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
@@ -519,7 +519,7 @@ func TestUpdatePublicDashboard(t *testing.T) {
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
@@ -588,7 +588,7 @@ func TestUpdatePublicDashboard(t *testing.T) {
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
@@ -686,7 +686,7 @@ func TestUpdatePublicDashboard(t *testing.T) {
quotaService := quotatest.New(false, nil)
dashboardStore, err := dashboardsDB.ProvideDashboardStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures(), tagimpl.ProvideService(sqlStore, sqlStore.Cfg), quotaService)
require.NoError(t, err)
publicdashboardStore := database.ProvideStore(sqlStore)
publicdashboardStore := database.ProvideStore(sqlStore, sqlStore.Cfg, featuremgmt.WithFeatures())
serviceWrapper := ProvideServiceWrapper(publicdashboardStore)
dashboard := insertTestDashboard(t, dashboardStore, "testDashie", 1, 0, true, []map[string]interface{}{}, nil)
@@ -915,152 +915,15 @@ func TestDashboardEnabledChanged(t *testing.T) {
func TestPublicDashboardServiceImpl_ListPublicDashboards(t *testing.T) {
type args struct {
ctx context.Context
u *user.SignedInUser
orgId int64
query *PublicDashboardListQuery
}
testCases := []struct {
name string
args args
evaluateFunc func(c context.Context, u *user.SignedInUser, e accesscontrol.Evaluator) (bool, error)
want []PublicDashboardListResponse
wantErr assert.ErrorAssertionFunc
}{
{
name: "should return empty list when user does not have permissions to read any dashboard",
args: args{
ctx: context.Background(),
u: &user.SignedInUser{OrgID: 1},
orgId: 1,
},
want: []PublicDashboardListResponse{},
wantErr: assert.NoError,
},
{
name: "should return all dashboards when has permissions",
args: args{
ctx: context.Background(),
u: &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{
1: {"dashboards:read": {
"dashboards:uid:0S6TmO67z", "dashboards:uid:1S6TmO67z", "dashboards:uid:2S6TmO67z", "dashboards:uid:9S6TmO67z",
}}},
},
orgId: 1,
},
want: []PublicDashboardListResponse{
{
Uid: "0GwW7mgVk",
AccessToken: "0b458cb7fe7f42c68712078bcacee6e3",
DashboardUid: "0S6TmO67z",
Title: "my zero dashboard",
IsEnabled: true,
},
{
Uid: "1GwW7mgVk",
AccessToken: "1b458cb7fe7f42c68712078bcacee6e3",
DashboardUid: "1S6TmO67z",
Title: "my first dashboard",
IsEnabled: true,
},
{
Uid: "2GwW7mgVk",
AccessToken: "2b458cb7fe7f42c68712078bcacee6e3",
DashboardUid: "2S6TmO67z",
Title: "my second dashboard",
IsEnabled: false,
},
{
Uid: "9GwW7mgVk",
AccessToken: "deletedashboardaccesstoken",
DashboardUid: "9S6TmO67z",
Title: "",
IsEnabled: true,
},
},
wantErr: assert.NoError,
},
{
name: "should return only dashboards with permissions",
args: args{
ctx: context.Background(),
u: &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{
1: {"dashboards:read": {"dashboards:uid:0S6TmO67z"}}},
},
orgId: 1,
},
want: []PublicDashboardListResponse{
{
Uid: "0GwW7mgVk",
AccessToken: "0b458cb7fe7f42c68712078bcacee6e3",
DashboardUid: "0S6TmO67z",
Title: "my zero dashboard",
IsEnabled: true,
},
},
wantErr: assert.NoError,
},
{
name: "should return orphaned public dashboards",
args: args{
ctx: context.Background(),
u: &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{
1: {"dashboards:read": {"dashboards:uid:0S6TmO67z"}}},
},
orgId: 1,
},
evaluateFunc: func(c context.Context, u *user.SignedInUser, e accesscontrol.Evaluator) (bool, error) {
return false, dashboards.ErrDashboardNotFound
},
want: []PublicDashboardListResponse{
{
Uid: "0GwW7mgVk",
AccessToken: "0b458cb7fe7f42c68712078bcacee6e3",
DashboardUid: "0S6TmO67z",
Title: "my zero dashboard",
IsEnabled: true,
},
{
Uid: "1GwW7mgVk",
AccessToken: "1b458cb7fe7f42c68712078bcacee6e3",
DashboardUid: "1S6TmO67z",
Title: "my first dashboard",
IsEnabled: true,
},
{
Uid: "2GwW7mgVk",
AccessToken: "2b458cb7fe7f42c68712078bcacee6e3",
DashboardUid: "2S6TmO67z",
Title: "my second dashboard",
IsEnabled: false,
},
{
Uid: "9GwW7mgVk",
AccessToken: "deletedashboardaccesstoken",
DashboardUid: "9S6TmO67z",
Title: "",
IsEnabled: true,
},
},
wantErr: assert.NoError,
},
{
name: "errors different than not data found should be returned",
args: args{
ctx: context.Background(),
u: &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{
1: {"dashboards:read": {"dashboards:uid:0S6TmO67z"}}},
},
orgId: 1,
},
evaluateFunc: func(c context.Context, u *user.SignedInUser, e accesscontrol.Evaluator) (bool, error) {
return false, dashboards.ErrDashboardCorrupt
},
want: nil,
wantErr: assert.Error,
},
type mockResponse struct {
PublicDashboardListResponseWithPagination *PublicDashboardListResponseWithPagination
Err error
}
mockedDashboards := []PublicDashboardListResponse{
mockedDashboards := []*PublicDashboardListResponse{
{
Uid: "0GwW7mgVk",
AccessToken: "0b458cb7fe7f42c68712078bcacee6e3",
@@ -1091,9 +954,62 @@ func TestPublicDashboardServiceImpl_ListPublicDashboards(t *testing.T) {
},
}
store := NewFakePublicDashboardStore(t)
store.On("FindAll", mock.Anything, mock.Anything).
Return(mockedDashboards, nil)
testCases := []struct {
name string
args args
want *PublicDashboardListResponseWithPagination
mockResponse *mockResponse
wantErr assert.ErrorAssertionFunc
}{
{
name: "should return correct pagination response",
args: args{
ctx: context.Background(),
query: &PublicDashboardListQuery{
User: &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{
1: {"dashboards:read": {"dashboards:uid:0S6TmO67z"}}},
},
OrgID: 1,
Page: 1,
Limit: 50,
},
},
mockResponse: &mockResponse{
PublicDashboardListResponseWithPagination: &PublicDashboardListResponseWithPagination{
TotalCount: int64(len(mockedDashboards)),
PublicDashboards: mockedDashboards,
},
Err: nil,
},
want: &PublicDashboardListResponseWithPagination{
Page: 1,
PerPage: 50,
TotalCount: int64(len(mockedDashboards)),
PublicDashboards: mockedDashboards,
},
wantErr: assert.NoError,
},
{
name: "should return error when store returns error",
args: args{
ctx: context.Background(),
query: &PublicDashboardListQuery{
User: &user.SignedInUser{OrgID: 1, Permissions: map[int64]map[string][]string{
1: {"dashboards:read": {"dashboards:uid:0S6TmO67z"}}},
},
OrgID: 1,
Page: 1,
Limit: 50,
},
},
mockResponse: &mockResponse{
PublicDashboardListResponseWithPagination: nil,
Err: errors.New("an err"),
},
want: nil,
wantErr: assert.Error,
},
}
ac := tests.SetupMockAccesscontrol(t,
func(c context.Context, siu *user.SignedInUser, _ accesscontrol.Options) ([]accesscontrol.Permission, error) {
@@ -1102,21 +1018,23 @@ func TestPublicDashboardServiceImpl_ListPublicDashboards(t *testing.T) {
false,
)
pd := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: store,
ac: ac,
}
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
ac.EvaluateFunc = tt.evaluateFunc
store := NewFakePublicDashboardStore(t)
store.On("FindAllWithPagination", mock.Anything, mock.Anything).
Return(tt.mockResponse.PublicDashboardListResponseWithPagination, tt.mockResponse.Err)
got, err := pd.FindAll(tt.args.ctx, tt.args.u, tt.args.orgId)
if !tt.wantErr(t, err, fmt.Sprintf("FindAll(%v, %v, %v)", tt.args.ctx, tt.args.u, tt.args.orgId)) {
pd := &PublicDashboardServiceImpl{
log: log.New("test.logger"),
store: store,
ac: ac,
}
got, err := pd.FindAllWithPagination(tt.args.ctx, tt.args.query)
if !tt.wantErr(t, err, fmt.Sprintf("FindAllWithPagination(%v, %v)", tt.args.ctx, tt.args.query)) {
return
}
assert.Equalf(t, tt.want, got, "FindAll(%v, %v, %v)", tt.args.ctx, tt.args.u, tt.args.orgId)
assert.Equalf(t, tt.want, got, "FindAllWithPagination(%v, %v)", tt.args.ctx, tt.args.query)
})
}
}