From d1370d2c0b77d381200c85568bb837e57b3f7a48 Mon Sep 17 00:00:00 2001
From: Tymoteusz Motylewski <t.motylewski@gmail.com>
Date: Mon, 30 Mar 2020 12:55:00 +0200
Subject: [PATCH] [TASK] Provide test for Page\TreeController

Add test for TreeController, so we're safe when refactoring
or doing performance optimizations.

Besides that moved pages in a workspace were not considered when
calculating permissions on the rootline due to missing workspace
overlays.

Resolves: #90831
Releases: 9.5, master
Change-Id: Ic3ab08d2502e8c9a3f08e737552c2e1d2a56a66c
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/63848
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Oliver Hader <oliver.hader@typo3.org>
Reviewed-by: Oliver Hader <oliver.hader@typo3.org>
---
 composer.json                                 |   2 +-
 composer.lock                                 |  18 +-
 .../Page/Fixtures/PagesWithBEPermissions.yaml | 137 ++++++
 .../Controller/Page/TreeControllerTest.php    | 434 ++++++++++++++++++
 .../BackendUserAuthentication.php             |   2 +-
 typo3/sysext/core/composer.json               |   2 +-
 6 files changed, 583 insertions(+), 12 deletions(-)
 create mode 100644 typo3/sysext/backend/Tests/Functional/Controller/Page/Fixtures/PagesWithBEPermissions.yaml
 create mode 100644 typo3/sysext/backend/Tests/Functional/Controller/Page/TreeControllerTest.php

diff --git a/composer.json b/composer.json
index 55d2b407d457..aa25556cecef 100644
--- a/composer.json
+++ b/composer.json
@@ -88,7 +88,7 @@
 		"phpstan/phpstan": "^0.12.13",
 		"rector/rector": "~0.7",
 		"typo3/cms-styleguide": "~10.0.2",
-		"typo3/testing-framework": "^6.2.0"
+		"typo3/testing-framework": "^6.2.2"
 	},
 	"suggest": {
 		"ext-gd": "GDlib/Freetype is required for building images with text (GIFBUILDER) and can also be used to scale images",
diff --git a/composer.lock b/composer.lock
index e84c6448a189..b8d3bc50c3e2 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "e6e138201c43bbc6250fbd836633a21d",
+    "content-hash": "cd0e12abcab99e0526c2f9707df4f2d1",
     "packages": [
         {
             "name": "cogpowered/finediff",
@@ -8115,8 +8115,8 @@
             "authors": [
                 {
                     "name": "Arne Blankerts",
-                    "email": "arne@blankerts.de",
-                    "role": "Developer"
+                    "role": "Developer",
+                    "email": "arne@blankerts.de"
                 }
             ],
             "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
@@ -8259,16 +8259,16 @@
         },
         {
             "name": "typo3/testing-framework",
-            "version": "6.2.0",
+            "version": "6.2.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/TYPO3/testing-framework.git",
-                "reference": "d308d4263b3268873c8b4028d8b526f2862aca6c"
+                "reference": "4e14e564f72875ce4c4414dd390c4b5ddb071fe6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/TYPO3/testing-framework/zipball/d308d4263b3268873c8b4028d8b526f2862aca6c",
-                "reference": "d308d4263b3268873c8b4028d8b526f2862aca6c",
+                "url": "https://api.github.com/repos/TYPO3/testing-framework/zipball/4e14e564f72875ce4c4414dd390c4b5ddb071fe6",
+                "reference": "4e14e564f72875ce4c4414dd390c4b5ddb071fe6",
                 "shasum": ""
             },
             "require": {
@@ -8280,7 +8280,7 @@
                 "typo3/cms-fluid": "10.*.*@dev",
                 "typo3/cms-frontend": "10.*.*@dev",
                 "typo3/cms-recordlist": "10.*.*@dev",
-                "typo3fluid/fluid": "^2.5"
+                "typo3fluid/fluid": "^2.5|^3"
             },
             "suggest": {
                 "codeception/codeception": "^4.0",
@@ -8315,7 +8315,7 @@
                 "tests",
                 "typo3"
             ],
-            "time": "2020-03-03T13:18:50+00:00"
+            "time": "2020-04-06T15:19:47+00:00"
         }
     ],
     "aliases": [],
diff --git a/typo3/sysext/backend/Tests/Functional/Controller/Page/Fixtures/PagesWithBEPermissions.yaml b/typo3/sysext/backend/Tests/Functional/Controller/Page/Fixtures/PagesWithBEPermissions.yaml
new file mode 100644
index 000000000000..a5eb0a3c589e
--- /dev/null
+++ b/typo3/sysext/backend/Tests/Functional/Controller/Page/Fixtures/PagesWithBEPermissions.yaml
@@ -0,0 +1,137 @@
+__variables:
+  - &pageStandard 0
+  - &pageShortcut 4
+  - &pageMount 7
+  - &pageFolder 254
+  - &contentText 'text'
+  - &idAcmeRootPage 1000
+  - &idAcmeFirstPage 1100
+
+entitySettings:
+  '*':
+    nodeColumnName: 'pid'
+    columnNames: {id: 'uid', language: 'sys_language_uid'}
+    defaultValues: {pid: 0}
+  page:
+    isNode: true
+    tableName: 'pages'
+    parentColumnName: 'pid'
+    languageColumnNames: ['l10n_parent', 'l10n_source']
+    columnNames: {type: 'doktype', root: 'is_siteroot', mount: 'mount_pid', visitorGroups: 'fe_group'}
+    defaultValues: {hidden: 0, doktype: *pageStandard, perms_userid: 1, perms_groupid: 9}
+    valueInstructions:
+      shortcut:
+        first: {shortcut: 0, shortcut_mode: 1}
+  content:
+    tableName: 'tt_content'
+    languageColumnNames: ['l18n_parent', 'l10n_source']
+    columnNames: {title: 'header', type: 'CType'}
+  workspace:
+    tableName: 'sys_workspace'
+  language:
+    tableName: 'sys_language'
+    columnNames: {code: 'language_isocode'}
+  visitorGroup:
+    tableName: 'fe_groups'
+  visitor:
+    tableName: 'fe_users'
+    columnNames: {groups: 'usergroup'}
+  typoscript:
+    tableName: 'sys_template'
+    valueInstructions:
+      type:
+        site: {root: 1, clear: 1}
+  beGroup:
+    tableName: 'be_groups'
+
+entities:
+  workspace:
+    - self: {id: 1, title: 'Workspace'}
+  language:
+    - self: {id: 1, title: 'French', code: 'fr'}
+    - self: {id: 2, title: 'Franco-Canadian', code: 'fr'}
+    - self: {id: 3, title: 'Spanish', code: 'es'}
+  beGroup:
+    - self: {id: 9, title: 'editors', db_mountpoints: '1000,2000', tables_select: 'pages,tt_content', tables_modify: 'pages,tt_content', page_types_select: '1,4,7,254', groupMods: 'web_layout,web_list'}
+  page:
+    - self: {id: *idAcmeRootPage, title: 'ACME Inc', type: *pageShortcut, shortcut: 'first', root: true, slug: '/'}
+      children:
+        - self: {id: *idAcmeFirstPage, title: 'EN: Welcome', slug: '/welcome', subtitle: 'hello-and-welcome'}
+        - self: {id: 1200, title: 'EN: Features', slug: '/features'}
+          children:
+            - self: {id: 1210, title: 'EN: Frontend Editing', slug: '/features/frontend-editing', perms_userid: 9, perms_groupid: 1, description: "accessible for user, but not for group"}
+            - self: {id: 1220, title: 'EN: Managing backend', slug: '/features/managing-backend', perms_userid: 1, perms_groupid: 1, description: "not accessible"}
+            - self: {id: 1230, title: 'EN: Managing content', slug: '/features/managing-content', perms_userid: 9, perms_groupid: 9, description: "accessible for user and group"}
+            - version: {id: 1240, title: 'EN: Managing data', slug: '/features/managing-data', workspace: 1}
+              children:
+                - version: {id: 124010, title: 'EN: Managing complex data', slug: '/features/managing-data/complex', workspace: 1}
+        - self: {id: 1400, title: 'EN: ACME in your Region', root: true, slug: '/acme-in-your-region'}
+          languageVariants:
+            - self: {id: 1401, title: 'FR: ACME in your Region', language: 1, slug: '/acme-dans-votre-region'}
+            - self: {id: 1402, title: 'FR-CA: ACME in your Region', language: 2, slug: '/acme-dans-votre-quebec'}
+          children:
+            - self: {id: 1410, title: 'EN: Groups', slug: '/acme-in-your-region/groups', l18n_cfg: 1}
+              languageVariants:
+                - self: {id: 1411, title: 'FR: Groups', language: 1, slug: '/acme-dans-votre-region/groupes'}
+                - self: {id: 1412, title: 'FR-CA: Groups', language: 2, slug: '/acme-dans-votre-quebec/groupes'}
+        - self: {id: 1500, title: 'Internal', slug: '/my-acme'}
+          children:
+            - self: {id: 1510, title: 'Whitepapers', visitorGroups: -2, extendToSubpages: true, slug: '/my-acme/whitepapers', perms_userid: 1, perms_groupid: 1, description: "not accessible"}
+              children:
+                - self: {id: 1511, title: 'Products', slug: '/my-acme/whitepapers/products'}
+                  children:
+                    - self: {id: 151110, title: 'Product 1', slug: '/my-acme/whitepapers/products/product-1'}
+                  versionVariants:
+                    - version: { workspace: 1 }
+                      actions:
+                        - { action: 'move', type: 'toPage', target: 1700 }
+                - self: {id: 1512, title: 'Solutions', visitorGroups: 10, slug: '/my-acme/whitepapers/solutions'}
+                  children:
+                    - self: {id: 151210, title: 'Solution 1', slug: '/my-acme/whitepapers/solutions/solution-1'}
+                - self: {id: 1515, title: 'Research', visitorGroups: 20, slug: '/my-acme/whitepapers/research'}
+            - self: {id: 1520, title: 'Forecasts', visitorGroups: 20, extendToSubpages: true, slug: '/my-acme/forecasts'}
+              children:
+                - self: {id: 1521, title: 'Current Year', slug: '/my-acme/forecasts/current-year'}
+                - self: {id: 1522, title: 'Next Year', slug: '/my-acme/forecasts/next-year'}
+                - self: {id: 1523, title: 'Five Years', slug: '/my-acme/forecasts/five-years'}
+            - self: {id: 1530, title: 'Reports', visitorGroups: 20, extendToSubpages: true, slug: '/my-acme/reports'}
+          languageVariants:
+            - version: {title: 'FR: Interne', workspace: 1, language: 1, slug: '/my-acme'}
+        - self: {id: 1600, title: 'About us', slug: '/about', perms_userid: 1, perms_groupid: 1, description: "not accessible"}
+        - self: {id: 1700, title: 'Announcements & News', type: *pageMount, mount: 7100, slug: '/news'}
+        - self: {id: 404, title: 'Page not found', slug: '/404'}
+          entities:
+            content:
+              - self: {title: 'EN: Page not found', type: *contentText}
+        - self: {id: 1930, title: 'Our Blog', type: *pageShortcut, shortcut: 2000, slug: '/blog'}
+        - version: {id: 1950, title: 'EN: Goodbye', workspace: 1, slug: '/bye'}
+          children:
+            - version: {title: 'EN: Really Goodbye', workspace: 1, slug: '/bye/bye'}
+        - self: {id: 1990, title: 'Storage', type: *pageFolder, slug: '/internal/storage'}
+          entities:
+            visitorGroup:
+              - self: {id: 10, title: 'Customers'}
+              - self: {id: 20, title: 'Partners'}
+            visitor:
+              - self: {id: 1, username: 'john@doe.local', groups: '10'}
+              - self: {id: 2, username: 'manager@other-inc.local', groups: '20'}
+              - self: {id: 3, username: 'big-boss@acme-inc.local', groups: '10,20'}
+    - self: {id: 2000, title: 'ACME Blog', type: *pageShortcut, shortcut: 'first', root: true, slug: '/', perms_userid: 1, perms_groupid: 1, description: "not accessible"}
+      children:
+        - self: {id: 2100, title: 'Authors', slug: '/authors'}
+          children:
+            - self: {id: 2110, title: 'John Doe', slug: '/john'}
+              children:
+                - self: {id: 2111, title: 'About', slug: '/about-john'}
+            - self: {id: 2120, title: 'Jane Doe', slug: '/jane'}
+              children:
+                - self: {id: 2121, title: 'About', slug: '/about-jane'}
+        - self: {id: 2700, title: 'Announcements & News', type: *pageMount, mount: 7100, slug: '/news'}
+        - self: {id: 2930, title: 'ACME Inc', type: *pageShortcut, shortcut: 1000, slug: '/acme'}
+    - self: {id: 7000, title: 'Common Collection', type: *pageFolder, root: true, slug: '/common'}
+      children:
+        - self: {id: 7100, title: 'Announcements & News', slug: '/common/news'}
+          children:
+            - self: {id: 7110, title: 'Markets', slug: '/common/markets'}
+            - self: {id: 7120, title: 'Products', slug: '/common/products'}
+            - self: {id: 7130, title: 'Partners', slug: '/common/partners'}
diff --git a/typo3/sysext/backend/Tests/Functional/Controller/Page/TreeControllerTest.php b/typo3/sysext/backend/Tests/Functional/Controller/Page/TreeControllerTest.php
new file mode 100644
index 000000000000..51438c0d89e1
--- /dev/null
+++ b/typo3/sysext/backend/Tests/Functional/Controller/Page/TreeControllerTest.php
@@ -0,0 +1,434 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Backend\Tests\Functional\Controller\Page;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Backend\Controller\Page\TreeController;
+use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
+use TYPO3\CMS\Core\Context\Context;
+use TYPO3\CMS\Core\Context\UserAspect;
+use TYPO3\CMS\Core\Context\WorkspaceAspect;
+use TYPO3\CMS\Core\Core\Bootstrap;
+use TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\TestingFramework\Core\AccessibleObjectInterface;
+use TYPO3\TestingFramework\Core\Functional\Framework\DataHandling\Scenario\DataHandlerFactory;
+use TYPO3\TestingFramework\Core\Functional\Framework\DataHandling\Scenario\DataHandlerWriter;
+use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
+
+/**
+ * Test case for TYPO3\CMS\Backend\Controller\Page\TreeController
+ */
+class TreeControllerTest extends FunctionalTestCase
+{
+    use SiteBasedTestTrait;
+
+    /**
+     * @var string[]
+     */
+    protected $coreExtensionsToLoad = ['workspaces'];
+
+    /**
+     * @var TreeController|AccessibleObjectInterface
+     */
+    private $subject;
+
+    /**
+     * @var BackendUserAuthentication
+     */
+    private $backendUser;
+
+    /**
+     * @var BackendUserAuthentication
+     */
+    private $regularBackendUser;
+
+    /**
+     * @var Context
+     */
+    private $context;
+
+    /**
+     * The fixture which is used when initializing a backend user
+     *
+     * @var string
+     */
+    protected $backendUserFixture = 'EXT:core/Tests/Functional/Fixtures/be_users.xml';
+
+    protected function setUp(): void
+    {
+        parent::setUp();
+        //admin user for importing dataset
+        $this->backendUser = $this->setUpBackendUserFromFixture(1);
+        $this->setUpDatabase();
+
+        //regular editor, non admin
+        $this->backendUser = $this->setUpBackendUser(9);
+        $this->context = GeneralUtility::makeInstance(Context::class);
+        $this->context->setAspect('backend.user', GeneralUtility::makeInstance(UserAspect::class, $this->backendUser));
+
+        $this->subject = $this->getAccessibleMock(TreeController::class, ['dummy']);
+    }
+
+    protected function tearDown(): void
+    {
+        unset($this->subject, $this->backendUser, $this->context);
+        parent::tearDown();
+    }
+
+    protected function setUpDatabase()
+    {
+        Bootstrap::initializeLanguageObject();
+        $scenarioFile = __DIR__ . '/Fixtures/PagesWithBEPermissions.yaml';
+        $factory = DataHandlerFactory::fromYamlFile($scenarioFile);
+        $writer = DataHandlerWriter::withBackendUser($this->backendUser);
+        $writer->invokeFactory($factory);
+        static::failIfArrayIsNotEmpty(
+            $writer->getErrors()
+        );
+    }
+
+    /**
+     * @test
+     */
+    public function getAllEntryPointPageTrees()
+    {
+        $actual = $this->subject->_call('getAllEntryPointPageTrees');
+        $keepProperties = array_flip(['uid', 'title', '_children']);
+        $actual = $this->sortTreeArray($actual);
+        $actual = $this->normalizeTreeArray($actual, $keepProperties);
+
+        $expected = [
+            [
+                'uid' => 1000,
+                'title' => 'ACME Inc',
+                '_children' => [
+                    [
+                        'uid' => 1100,
+                        'title' => 'EN: Welcome',
+                        '_children' => [
+                        ],
+                    ],
+                    [
+                        'uid' => 1200,
+                        'title' => 'EN: Features',
+                        '_children' => [
+                            [
+                                'uid' => 1210,
+                                'title' => 'EN: Frontend Editing',
+                                '_children' => [
+                                ],
+                            ],
+                            [
+                                'uid' => 1230,
+                                'title' => 'EN: Managing content',
+                                '_children' => [
+                                ],
+                            ],
+                        ],
+                    ],
+                    [
+                        'uid' => 1400,
+                        'title' => 'EN: ACME in your Region',
+                        '_children' => [
+                            [
+                                'uid' => 1410,
+                                'title' => 'EN: Groups',
+                                '_children' => [
+                                ],
+                            ],
+                        ],
+                    ],
+                    [
+                        'uid' => 1500,
+                        'title' => 'Internal',
+                        '_children' => [
+                            [
+                                'uid' => 1520,
+                                'title' => 'Forecasts',
+                                '_children' => [
+                                    [
+                                        'uid' => 1521,
+                                        'title' => 'Current Year',
+                                        '_children' => [
+                                        ],
+                                    ],
+                                    [
+                                        'uid' => 1522,
+                                        'title' => 'Next Year',
+                                        '_children' => [
+                                        ],
+                                    ],
+                                    [
+                                        'uid' => 1523,
+                                        'title' => 'Five Years',
+                                        '_children' => [
+                                        ],
+                                    ],
+                                ],
+                            ],
+                            [
+                                'uid' => 1530,
+                                'title' => 'Reports',
+                                '_children' => [
+                                ],
+                            ],
+                        ],
+                    ],
+                    [
+                        'uid' => 1700,
+                        'title' => 'Announcements & News',
+                        '_children' => [
+                        ],
+                    ],
+                    [
+                        'uid' => 404,
+                        'title' => 'Page not found',
+                        '_children' => [
+                        ],
+                    ],
+                    [
+                        'uid' => 1930,
+                        'title' => 'Our Blog',
+                        '_children' => [
+                        ],
+                    ],
+                    [
+                        'uid' => 1990,
+                        'title' => 'Storage',
+                        '_children' => [
+                        ],
+                    ],
+                ],
+            ],
+        ];
+        self::assertEquals($expected, $actual);
+    }
+
+    /**
+     * @test
+     */
+    public function getAllEntryPointPageTreesInWorkspace()
+    {
+        $this->setWorkspace(1);
+        $actual = $this->subject->_call('getAllEntryPointPageTrees');
+        $keepProperties = array_flip(['uid', 'title', '_children']);
+        $actual = $this->sortTreeArray($actual);
+        $actual = $this->normalizeTreeArray($actual, $keepProperties);
+
+        $expected = [
+            [
+                'uid' => 1000,
+                'title' => 'ACME Inc',
+                '_children' => [
+                    [
+                        'uid' => 1950,
+                        'title' => 'EN: Goodbye',
+                        '_children' => [
+                            [
+                                'uid' => 10007,
+                                'title' => 'EN: Really Goodbye',
+                                '_children' => [
+                                ],
+                            ],
+                        ],
+                    ],
+                    [
+                        'uid' => 1100,
+                        'title' => 'EN: Welcome',
+                        '_children' => [
+                        ],
+                    ],
+                    [
+                        'uid' => 1200,
+                        'title' => 'EN: Features',
+                        '_children' => [
+                            [
+                                'uid' => 1240,
+                                'title' => 'EN: Managing data',
+                                '_children' => [
+                                    [
+                                        'uid' => 124010,
+                                        'title' => 'EN: Managing complex data',
+                                        '_children' => [
+                                        ],
+                                    ],
+                                ],
+                            ],
+                            [
+                                'uid' => 1210,
+                                'title' => 'EN: Frontend Editing',
+                                '_children' => [
+                                ],
+                            ],
+                            [
+                                'uid' => 1230,
+                                'title' => 'EN: Managing content',
+                                '_children' => [
+                                ],
+                            ],
+                        ],
+                    ],
+                    [
+                        'uid' => 1400,
+                        'title' => 'EN: ACME in your Region',
+                        '_children' => [
+                            [
+                                'uid' => 1410,
+                                'title' => 'EN: Groups',
+                                '_children' => [
+                                ],
+                            ],
+                        ],
+                    ],
+                    [
+                        'uid' => 1500,
+                        'title' => 'Internal',
+                        '_children' => [
+                            [
+                                'uid' => 1520,
+                                'title' => 'Forecasts',
+                                '_children' => [
+                                    [
+                                        'uid' => 1521,
+                                        'title' => 'Current Year',
+                                        '_children' => [
+                                        ],
+                                    ],
+                                    [
+                                        'uid' => 1522,
+                                        'title' => 'Next Year',
+                                        '_children' => [
+                                        ],
+                                    ],
+                                    [
+                                        'uid' => 1523,
+                                        'title' => 'Five Years',
+                                        '_children' => [
+                                        ],
+                                    ],
+                                ],
+                            ],
+                            [
+                                'uid' => 1530,
+                                'title' => 'Reports',
+                                '_children' => [
+                                ],
+                            ],
+                        ],
+                    ],
+                    [
+                        'uid' => 1700,
+                        'title' => 'Announcements & News',
+                        '_children' => [
+                            [
+                                // page with sub-pages moved in workspace 1
+                                // from pid 1510 (missing permissions) to pid 1700 (visible now)
+                                'uid' => 1511,
+                                'title' => 'Products',
+                                '_children' => [
+                                    [
+                                        'uid' => 151110,
+                                        'title' => 'Product 1',
+                                        '_children' => [],
+                                    ]
+                                ],
+                            ],
+                        ],
+                    ],
+                    [
+                        'uid' => 404,
+                        'title' => 'Page not found',
+                        '_children' => [
+                        ],
+                    ],
+                    [
+                        'uid' => 1930,
+                        'title' => 'Our Blog',
+                        '_children' => [
+                        ],
+                    ],
+                    [
+                        'uid' => 1990,
+                        'title' => 'Storage',
+                        '_children' => [
+                        ],
+                    ],
+                ],
+            ],
+        ];
+        self::assertEquals($expected, $actual);
+    }
+
+    /**
+     * @param int $workspaceId
+     */
+    private function setWorkspace(int $workspaceId)
+    {
+        $this->backendUser->workspace = $workspaceId;
+        $this->context->setAspect('workspace', new WorkspaceAspect($workspaceId));
+    }
+
+    /**
+     * Sorts tree array by index of each section item recursively.
+     *
+     * @param array $tree
+     * @return array
+     */
+    private function sortTreeArray(array $tree): array
+    {
+        ksort($tree);
+        return array_map(
+            function (array $item) {
+                foreach ($item as $propertyName => $propertyValue) {
+                    if (!is_array($propertyValue)) {
+                        continue;
+                    }
+                    $item[$propertyName] = $this->sortTreeArray($propertyValue);
+                }
+                return $item;
+            },
+            $tree
+        );
+    }
+
+    /**
+     * Normalizes a tree array, re-indexes numeric indexes, only keep given properties.
+     *
+     * @param array $tree Whole tree array
+     * @param array $keepProperties (property names to be used as indexes for array_intersect_key())
+     * @return array Normalized tree array
+     */
+    private function normalizeTreeArray(array $tree, array $keepProperties): array
+    {
+        return array_map(
+            function (array $item) use ($keepProperties) {
+                // only keep these property names
+                $item = array_intersect_key($item, $keepProperties);
+                foreach ($item as $propertyName => $propertyValue) {
+                    if (!is_array($propertyValue)) {
+                        continue;
+                    }
+                    // process recursively for nested array items (e.g. `_children`)
+                    $item[$propertyName] = $this->normalizeTreeArray($propertyValue, $keepProperties);
+                }
+                return $item;
+            },
+            // normalize numeric indexes (remove sorting markers)
+            array_values($tree)
+        );
+    }
+}
diff --git a/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php b/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php
index 00508fc55d3a..ec15308b8abe 100644
--- a/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php
+++ b/typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php
@@ -413,7 +413,7 @@ class BackendUserAuthentication extends AbstractUserAuthentication
         }
         if ($id > 0) {
             $wM = $this->returnWebmounts();
-            $rL = BackendUtility::BEgetRootLine($id, ' AND ' . $readPerms);
+            $rL = BackendUtility::BEgetRootLine($id, ' AND ' . $readPerms, true);
             foreach ($rL as $v) {
                 if ($v['uid'] && in_array($v['uid'], $wM)) {
                     return $v['uid'];
diff --git a/typo3/sysext/core/composer.json b/typo3/sysext/core/composer.json
index 937264cc0b68..69c764090504 100644
--- a/typo3/sysext/core/composer.json
+++ b/typo3/sysext/core/composer.json
@@ -69,7 +69,7 @@
 		"phpspec/prophecy": "^1.7.5",
 		"phpstan/phpstan": "^0.12.13",
 		"typo3/cms-styleguide": "~10.0.2",
-		"typo3/testing-framework": "^6.2.0"
+		"typo3/testing-framework": "^6.2.2"
 	},
 	"suggest": {
 		"ext-fileinfo": "Used for proper file type detection in the file abstraction layer",
-- 
GitLab