Skip to content

Commit 3432f2b

Browse files
committed
Merge remote-tracking branch 'origin/MAGETWO-95365' into 2.2.7-develop-pr53
2 parents 04984e1 + f3655ca commit 3432f2b

File tree

4 files changed

+200
-12
lines changed

4 files changed

+200
-12
lines changed

app/code/Magento/Ui/Controller/Adminhtml/Index/Render/Handle.php

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,88 @@
99
use Magento\Ui\Component\Control\ActionPool;
1010
use Magento\Ui\Component\Wrapper\UiComponent;
1111
use Magento\Ui\Controller\Adminhtml\AbstractAction;
12+
use Magento\Backend\App\Action\Context;
13+
use Magento\Framework\View\Element\UiComponentFactory;
14+
use Magento\Framework\View\Element\UiComponent\ContextFactory;
15+
use Magento\Framework\App\ObjectManager;
1216

1317
/**
1418
* Class Handle
1519
*/
1620
class Handle extends AbstractAction
1721
{
22+
/**
23+
* @var ContextFactory
24+
*/
25+
private $contextFactory;
26+
27+
/**
28+
* @param Context $context
29+
* @param UiComponentFactory $factory
30+
* @param ContextFactory|null $contextFactory
31+
*/
32+
public function __construct(
33+
Context $context,
34+
UiComponentFactory $factory,
35+
ContextFactory $contextFactory = null
36+
) {
37+
parent::__construct($context, $factory);
38+
$this->contextFactory = $contextFactory
39+
?: ObjectManager::getInstance()->get(ContextFactory::class);
40+
}
41+
1842
/**
1943
* Render UI component by namespace in handle context
2044
*
2145
* @return void
2246
*/
2347
public function execute()
2448
{
49+
$response = '';
2550
$handle = $this->_request->getParam('handle');
2651
$namespace = $this->_request->getParam('namespace');
2752
$buttons = $this->_request->getParam('buttons', false);
28-
2953
$this->_view->loadLayout(['default', $handle], true, true, false);
54+
$layout = $this->_view->getLayout();
55+
$context = $this->contextFactory->create(
56+
[
57+
'namespace' => $namespace,
58+
'pageLayout' => $layout,
59+
]
60+
);
3061

31-
$uiComponent = $this->_view->getLayout()->getBlock($namespace);
32-
$response = $uiComponent instanceof UiComponent ? $uiComponent->toHtml() : '';
62+
$component = $this->factory->create($namespace, null, ['context' => $context]);
63+
if ($this->validateAclResource($component->getContext()->getDataProvider()->getConfigData())) {
64+
$uiComponent = $layout->getBlock($namespace);
65+
$response = $uiComponent instanceof UiComponent ? $uiComponent->toHtml() : '';
66+
}
3367

3468
if ($buttons) {
35-
$actionsToolbar = $this->_view->getLayout()->getBlock(ActionPool::ACTIONS_PAGE_TOOLBAR);
69+
$actionsToolbar = $layout->getBlock(ActionPool::ACTIONS_PAGE_TOOLBAR);
3670
$response .= $actionsToolbar instanceof Template ? $actionsToolbar->toHtml() : '';
3771
}
3872

3973
$this->_response->appendBody($response);
4074
}
75+
76+
/**
77+
* Optionally validate ACL resource of components with a DataSource/DataProvider
78+
*
79+
* @param mixed $dataProviderConfigData
80+
* @return bool
81+
*/
82+
private function validateAclResource($dataProviderConfigData): bool
83+
{
84+
if (isset($dataProviderConfigData['aclResource'])
85+
&& !$this->_authorization->isAllowed($dataProviderConfigData['aclResource'])
86+
) {
87+
if (!$this->_request->isAjax()) {
88+
$this->_redirect('admin/denied');
89+
}
90+
91+
return false;
92+
}
93+
94+
return true;
95+
}
4196
}

app/code/Magento/Ui/Test/Unit/Controller/Adminhtml/Index/Render/HandleTest.php

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@
66
namespace Magento\Ui\Test\Unit\Controller\Adminhtml\Index\Render;
77

88
use Magento\Ui\Controller\Adminhtml\Index\Render\Handle;
9+
use Magento\Framework\View\Element\UiComponent\ContextInterface;
910

11+
/**
12+
* Tests \Magento\Ui\Controller\Adminhtml\Index\Render\Handle.
13+
*
14+
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
15+
*/
1016
class HandleTest extends \PHPUnit\Framework\TestCase
1117
{
1218
/**
@@ -27,22 +33,42 @@ class HandleTest extends \PHPUnit\Framework\TestCase
2733
/**
2834
* @var \PHPUnit_Framework_MockObject_MockObject
2935
*/
30-
protected $componentFactoryMock;
36+
protected $viewMock;
37+
38+
/**
39+
* @var Handle
40+
*/
41+
protected $controller;
42+
43+
/**
44+
* @var \Magento\Framework\AuthorizationInterface|\PHPUnit_Framework_MockObject_MockObject
45+
*/
46+
private $authorizationMock;
3147

3248
/**
3349
* @var \PHPUnit_Framework_MockObject_MockObject
3450
*/
35-
protected $viewMock;
51+
private $uiComponentContextMock;
3652

3753
/**
38-
* @var Handle
54+
* @var \Magento\Framework\View\Element\UiComponentInterface|\PHPUnit_Framework_MockObject_MockObject
3955
*/
40-
protected $controller;
56+
private $uiComponentMock;
57+
58+
/**
59+
* @var \PHPUnit_Framework_MockObject_MockObject
60+
*/
61+
private $uiFactoryMock;
62+
63+
/**
64+
* @var \Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface|
65+
* \PHPUnit_Framework_MockObject_MockObject
66+
*/
67+
private $dataProviderMock;
4168

4269
public function setUp()
4370
{
4471
$this->contextMock = $this->createMock(\Magento\Backend\App\Action\Context::class);
45-
$this->componentFactoryMock = $this->createMock(\Magento\Framework\View\Element\UiComponentFactory::class);
4672

4773
$this->requestMock = $this->createMock(\Magento\Framework\App\RequestInterface::class);
4874
$this->contextMock->expects($this->atLeastOnce())->method('getRequest')->willReturn($this->requestMock);
@@ -52,8 +78,34 @@ public function setUp()
5278

5379
$this->viewMock = $this->createMock(\Magento\Framework\App\ViewInterface::class);
5480
$this->contextMock->expects($this->atLeastOnce())->method('getView')->willReturn($this->viewMock);
55-
56-
$this->controller = new Handle($this->contextMock, $this->componentFactoryMock);
81+
$this->authorizationMock = $this->getMockForAbstractClass(\Magento\Framework\AuthorizationInterface::class);
82+
$this->authorizationMock->expects($this->any())
83+
->method('isAllowed')
84+
->willReturn(true);
85+
$this->uiComponentContextMock = $this->getMockForAbstractClass(ContextInterface::class);
86+
$this->uiComponentMock = $this->getMockForAbstractClass(
87+
\Magento\Framework\View\Element\UiComponentInterface::class
88+
);
89+
$this->dataProviderMock = $this->getMockForAbstractClass(
90+
\Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface::class
91+
);
92+
$this->uiComponentContextMock->expects($this->once())
93+
->method('getDataProvider')
94+
->willReturn($this->dataProviderMock);
95+
$this->uiFactoryMock = $this->getMockBuilder(\Magento\Framework\View\Element\UiComponentFactory::class)
96+
->disableOriginalConstructor()
97+
->getMock();
98+
$this->uiComponentMock->expects($this->any())
99+
->method('getContext')
100+
->willReturn($this->uiComponentContextMock);
101+
$this->uiFactoryMock->expects($this->any())
102+
->method('create')
103+
->willReturn($this->uiComponentMock);
104+
$this->dataProviderMock->expects($this->once())
105+
->method('getConfigData')
106+
->willReturn([]);
107+
$contextMock = $this->createMock(\Magento\Framework\View\Element\UiComponent\ContextFactory::class);
108+
$this->controller = new Handle($this->contextMock, $this->uiFactoryMock, $contextMock);
57109
}
58110

59111
public function testExecuteNoButtons()
@@ -83,7 +135,7 @@ public function testExecute()
83135
->with(['default', $result], true, true, false);
84136

85137
$layoutMock = $this->createMock(\Magento\Framework\View\LayoutInterface::class);
86-
$this->viewMock->expects($this->exactly(2))->method('getLayout')->willReturn($layoutMock);
138+
$this->viewMock->expects($this->once())->method('getLayout')->willReturn($layoutMock);
87139

88140
$layoutMock->expects($this->exactly(2))->method('getBlock');
89141

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Ui\Controller\Adminhtml\Index\Renderer;
9+
10+
use Magento\TestFramework\Helper\Bootstrap;
11+
use Magento\Framework\AuthorizationInterface;
12+
13+
/**
14+
* Test for \Magento\Ui\Controller\Adminhtml\Index\Render\Handle.
15+
*
16+
* @magentoAppArea adminhtml
17+
*/
18+
class HandleTest extends \Magento\TestFramework\TestCase\AbstractBackendController
19+
{
20+
/**
21+
* @magentoDataFixture Magento/Customer/_files/customer.php
22+
*/
23+
public function testExecuteWhenUserDoesNotHavePermission()
24+
{
25+
Bootstrap::getObjectManager()->configure([
26+
'preferences' => [
27+
AuthorizationInterface::class => \Magento\Ui\Model\AuthorizationMock::class,
28+
],
29+
]);
30+
$this->getRequest()->setParam('handle', 'customer_index_index');
31+
$this->getRequest()->setParam('namespace', 'customer_listing');
32+
$this->getRequest()->setParam('sorting%5Bfield%5D', 'entity_id');
33+
$this->getRequest()->setParam('paging%5BpageSize%5D', 20);
34+
$this->getRequest()->setParam('isAjax', 1);
35+
$this->dispatch('backend/mui/index/render_handle');
36+
$output = $this->getResponse()->getBody();
37+
$this->assertEmpty($output, 'The acl restriction wasn\'t applied properly');
38+
}
39+
40+
/**
41+
* @magentoDataFixture Magento/Customer/_files/customer.php
42+
*/
43+
public function testExecuteWhenUserHasPermission()
44+
{
45+
$this->getRequest()->setParam('handle', 'customer_index_index');
46+
$this->getRequest()->setParam('namespace', 'customer_listing');
47+
$this->getRequest()->setParam('sorting%5Bfield%5D', 'entity_id');
48+
$this->getRequest()->setParam('paging%5BpageSize%5D', 20);
49+
$this->getRequest()->setParam('isAjax', 1);
50+
$this->dispatch('backend/mui/index/render_handle');
51+
$output = $this->getResponse()->getBody();
52+
$this->assertNotEmpty($output, 'The acl restriction wasn\'t applied properly');
53+
}
54+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Ui\Model;
9+
10+
/**
11+
* Check current user permission on resource and privilege.
12+
*/
13+
class AuthorizationMock extends \Magento\Framework\Authorization
14+
{
15+
/**
16+
* Check current user permission on resource and privilege
17+
*
18+
* @param string $resource
19+
* @param string $privilege
20+
* @return boolean
21+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
22+
*/
23+
public function isAllowed($resource, $privilege = null)
24+
{
25+
return $resource !== 'Magento_Customer::manage';
26+
}
27+
}

0 commit comments

Comments
 (0)