Library panels: Move to integration tests (#108737)

This commit is contained in:
Stephanie Hingtgen
2025-07-28 11:38:10 -05:00
committed by GitHub
parent f41570a6f7
commit 5ef744aa20
2 changed files with 143 additions and 74 deletions
@@ -216,80 +216,6 @@ func TestIntegration_GetLibraryPanelConnections(t *testing.T) {
}
})
scenarioWithPanel(t, "When a user tries to get connections of library panel, dashboards in inaccessible folders should not be returned",
func(t *testing.T, sc scenarioContext) {
accessibleFolder := createFolder(t, sc, "AccessibleFolder", sc.service.folderService)
inaccessibleFolder := createFolder(t, sc, "InAccessibleFolder", sc.service.folderService)
restrictedUser := user.SignedInUser{
UserID: 2,
Name: "Non-Admin User",
Login: "non-admin-user",
OrgID: sc.user.OrgID,
OrgRole: org.RoleViewer,
LastSeenAt: time.Now(),
Permissions: map[int64]map[string][]string{
sc.user.OrgID: {
dashboards.ActionFoldersRead: {
dashboards.ScopeFoldersProvider.GetResourceScopeUID(accessibleFolder.UID),
},
dashboards.ActionDashboardsRead: {dashboards.ScopeDashboardsProvider.GetResourceScopeUID("*")},
},
},
}
command := getCreatePanelCommand(accessibleFolder.ID, accessibleFolder.UID, "Accessible Library Panel") // nolint:staticcheck
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
libraryElement := validateAndUnMarshalResponse(t, resp)
dashJSON := map[string]any{
"panels": []any{
map[string]any{
"id": int64(1),
"gridPos": map[string]any{
"h": 6,
"w": 6,
"x": 0,
"y": 0,
},
"libraryPanel": map[string]any{
"uid": libraryElement.Result.UID,
"name": libraryElement.Result.Name,
},
},
},
}
accessibleDash := dashboards.Dashboard{
Title: "Accessible Dashboard",
Data: simplejson.NewFromAny(dashJSON),
}
// create the dashboard in the general folder, an accessible folder, and an inaccessible folder
dashInGeneral := createDashboard(t, sc.sqlStore, restrictedUser, &accessibleDash, 0, "")
err := sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{libraryElement.Result.UID}, dashInGeneral.ID)
require.NoError(t, err)
dashInAccessibleFolder := createDashboard(t, sc.sqlStore, restrictedUser, &accessibleDash, 0, accessibleFolder.UID)
err = sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{libraryElement.Result.UID}, dashInAccessibleFolder.ID)
require.NoError(t, err)
dashInInaccessibleFolder := createDashboard(t, sc.sqlStore, restrictedUser, &accessibleDash, 0, inaccessibleFolder.UID)
err = sc.service.ConnectElementsToDashboard(sc.reqContext.Req.Context(), sc.reqContext.SignedInUser, []string{libraryElement.Result.UID}, dashInInaccessibleFolder.ID)
require.NoError(t, err)
sc.reqContext.SignedInUser = &restrictedUser
sc.ctx.Req = web.SetURLParams(sc.ctx.Req, map[string]string{":uid": libraryElement.Result.UID})
// connections should return the general folder one and the accessible folder one
connectionsResp := sc.service.getConnectionsHandler(sc.reqContext)
var result = validateAndUnMarshalConnectionResponse(t, connectionsResp)
require.Len(t, result.Result, 2)
uids := []string{result.Result[0].ConnectionUID, result.Result[1].ConnectionUID}
require.Contains(t, uids, dashInGeneral.UID)
require.Contains(t, uids, dashInAccessibleFolder.UID)
require.NotContains(t, uids, dashInInaccessibleFolder.UID)
})
scenarioWithPanel(t, "When an admin tries to create a connection with an element that exists, but the original folder does not, it should still succeed",
func(t *testing.T, sc scenarioContext) {
b, err := json.Marshal(map[string]string{"test": "test"})
@@ -12,6 +12,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/services/org"
"github.com/grafana/grafana/pkg/tests/apis"
"github.com/grafana/grafana/pkg/tests/testinfra"
)
@@ -292,3 +293,145 @@ func deleteLibraryElement(t *testing.T, ctx TestContext, user apis.User, uid str
return nil
}
func TestIntegrationLibraryPanelConnectionsWithFolderAccess(t *testing.T) {
dualWriterModes := []rest.DualWriterMode{rest.Mode0, rest.Mode1, rest.Mode2, rest.Mode3, rest.Mode4, rest.Mode5}
for _, dualWriterMode := range dualWriterModes {
t.Run(fmt.Sprintf("DualWriterMode %d", dualWriterMode), func(t *testing.T) {
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
DisableAnonymous: true,
EnableFeatureToggles: []string{
"unifiedStorageSearch",
"kubernetesLibraryPanels",
"kubernetesClientDashboardsFolders",
},
})
ctx := createTestContext(t, helper, helper.Org1, dualWriterMode)
accessibleFolder, err := createFolder(t, ctx.Helper, ctx.AdminUser, "AccessibleFolder")
require.NoError(t, err)
require.NotNil(t, accessibleFolder)
inaccessibleFolder, err := createFolder(t, ctx.Helper, ctx.AdminUser, "InAccessibleFolder")
require.NoError(t, err)
require.NotNil(t, inaccessibleFolder)
setResourceUserPermission(t, ctx, ctx.AdminUser, false, accessibleFolder.UID, addUserPermission(t, nil, ctx.ViewerUser, ResourcePermissionLevelView))
setResourceUserPermission(t, ctx, ctx.AdminUser, false, inaccessibleFolder.UID, []ResourcePermissionSetting{})
libraryElement := map[string]interface{}{
"kind": 1,
"name": "Accessible Library Panel",
"folderUid": accessibleFolder.UID,
"model": map[string]interface{}{
"type": "text",
"title": "Accessible Library Panel",
},
}
libraryElementURL := "/api/library-elements"
libraryElementData, err := postHelper(t, &ctx, libraryElementURL, libraryElement, ctx.AdminUser)
require.NoError(t, err)
require.NotNil(t, libraryElementData)
data := libraryElementData["result"].(map[string]interface{})
uid := data["uid"].(string)
require.NotEmpty(t, uid)
dashInGeneral := createDashboardObject(t, "Dashboard in General", "", 1)
dashInGeneral.Object["spec"].(map[string]interface{})["panels"] = []interface{}{
map[string]interface{}{
"id": 1,
"title": "Library Panel",
"type": "library-panel-ref",
"libraryPanel": map[string]interface{}{
"uid": uid,
"name": "Accessible Library Panel",
},
},
}
adminClient := getResourceClient(t, ctx.Helper, ctx.AdminUser, getDashboardGVR())
createdDashInGeneral, err := adminClient.Resource.Create(context.Background(), dashInGeneral, v1.CreateOptions{})
require.NoError(t, err)
require.NotNil(t, createdDashInGeneral)
dashInAccessibleFolder := createDashboardObject(t, "Dashboard in Accessible Folder", accessibleFolder.UID, 1)
dashInAccessibleFolder.Object["spec"].(map[string]interface{})["panels"] = []interface{}{
map[string]interface{}{
"id": 1,
"title": "Library Panel",
"type": "library-panel-ref",
"libraryPanel": map[string]interface{}{
"uid": uid,
"name": "Accessible Library Panel",
},
},
}
createdDashInAccessible, err := adminClient.Resource.Create(context.Background(), dashInAccessibleFolder, v1.CreateOptions{})
require.NoError(t, err)
require.NotNil(t, createdDashInAccessible)
dashInInaccessibleFolder := createDashboardObject(t, "Dashboard in Inaccessible Folder", inaccessibleFolder.UID, 1)
dashInInaccessibleFolder.Object["spec"].(map[string]interface{})["panels"] = []interface{}{
map[string]interface{}{
"id": 1,
"title": "Library Panel",
"type": "library-panel-ref",
"libraryPanel": map[string]interface{}{
"uid": uid,
"name": "Accessible Library Panel",
},
},
}
createdDashInInaccessible, err := adminClient.Resource.Create(context.Background(), dashInInaccessibleFolder, v1.CreateOptions{})
require.NoError(t, err)
require.NotNil(t, createdDashInInaccessible)
connectionsURL := fmt.Sprintf("/api/library-elements/%s/connections", uid)
connectionsData, err := getDashboardViaHTTP(t, &ctx, connectionsURL, ctx.AdminUser)
require.NoError(t, err)
require.NotNil(t, connectionsData)
connections := connectionsData["result"].([]interface{})
require.Len(t, connections, 3, "Admin should see all connections")
connectionUIDs := make([]string, 0, len(connections))
for _, conn := range connections {
connMap := conn.(map[string]interface{})
if connectionUID, ok := connMap["connectionUid"].(string); ok {
connectionUIDs = append(connectionUIDs, connectionUID)
}
}
generalDashUID := createdDashInGeneral.GetName()
accessibleDashUID := createdDashInAccessible.GetName()
inaccessibleDashUID := createdDashInInaccessible.GetName()
require.Contains(t, connectionUIDs, generalDashUID, "Admin should see dashboard in general folder")
require.Contains(t, connectionUIDs, accessibleDashUID, "Admin should see dashboard in accessible folder")
require.Contains(t, connectionUIDs, inaccessibleDashUID, "Admin should see dashboard in inaccessible folder")
limitedUser := ctx.Helper.CreateUser("limited-user", "Org1", org.RoleViewer, nil)
// can access accessibleFolder but not inaccessibleFolder
setResourceUserPermission(t, ctx, ctx.AdminUser, false, accessibleFolder.UID, addUserPermission(t, nil, limitedUser, ResourcePermissionLevelView))
setResourceUserPermission(t, ctx, ctx.AdminUser, false, inaccessibleFolder.UID, []ResourcePermissionSetting{})
connectionsDataLimited, err := getDashboardViaHTTP(t, &ctx, connectionsURL, limitedUser)
require.NoError(t, err)
require.NotNil(t, connectionsDataLimited)
connectionsLimited := connectionsDataLimited["result"].([]interface{})
require.Len(t, connectionsLimited, 2, "Limited user should only see connections to accessible dashboards")
connectionUIDsLimited := make([]string, 0, len(connectionsLimited))
for _, conn := range connectionsLimited {
connMap := conn.(map[string]interface{})
if connectionUID, ok := connMap["connectionUid"].(string); ok {
connectionUIDsLimited = append(connectionUIDsLimited, connectionUID)
}
}
require.Contains(t, connectionUIDsLimited, generalDashUID, "Limited user should see dashboard in general folder")
require.Contains(t, connectionUIDsLimited, accessibleDashUID, "Limited user should see dashboard in accessible folder")
require.NotContains(t, connectionUIDsLimited, inaccessibleDashUID, "Limited user should NOT see dashboard in inaccessible folder")
err = adminClient.Resource.Delete(context.Background(), createdDashInGeneral.GetName(), v1.DeleteOptions{})
require.NoError(t, err)
err = adminClient.Resource.Delete(context.Background(), createdDashInAccessible.GetName(), v1.DeleteOptions{})
require.NoError(t, err)
err = adminClient.Resource.Delete(context.Background(), createdDashInInaccessible.GetName(), v1.DeleteOptions{})
require.NoError(t, err)
})
}
}