Skip to content

Commit e89608b

Browse files
committed
MAGETWO-95364: Magento\Ui\Controller\Adminhtml\Index\Render\Handle controller
1 parent 2d9c1e6 commit e89608b

File tree

4 files changed

+303
-40
lines changed

4 files changed

+303
-40
lines changed

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

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,82 @@
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\UiComponent\Config\ManagerInterface;
14+
use Magento\Framework\View\Element\UiComponentFactory;
15+
use Magento\Framework\App\ObjectManager;
1216

1317
/**
1418
* Class Handle
1519
*/
1620
class Handle extends AbstractAction
1721
{
22+
/**
23+
* @var ManagerInterface
24+
*/
25+
private $componentManager;
26+
27+
/**
28+
* @param Context $context
29+
* @param UiComponentFactory $factory
30+
* @param ManagerInterface|null $componentManager
31+
*/
32+
public function __construct(
33+
Context $context,
34+
UiComponentFactory $factory,
35+
ManagerInterface $componentManager = null
36+
) {
37+
parent::__construct($context, $factory);
38+
$this->componentManager = $componentManager ?: ObjectManager::getInstance()->get(ManagerInterface::class);
39+
}
40+
1841
/**
1942
* Render UI component by namespace in handle context
2043
*
2144
* @return void
2245
*/
2346
public function execute()
2447
{
48+
$response = '';
2549
$handle = $this->_request->getParam('handle');
2650
$namespace = $this->_request->getParam('namespace');
2751
$buttons = $this->_request->getParam('buttons', false);
28-
2952
$this->_view->loadLayout(['default', $handle], true, true, false);
53+
$layout = $this->_view->getLayout();
54+
$config = $this->componentManager->getData($namespace);
3055

31-
$uiComponent = $this->_view->getLayout()->getBlock($namespace);
32-
$response = $uiComponent instanceof UiComponent ? $uiComponent->toHtml() : '';
56+
if ($this->validateAclResource($config[$namespace])) {
57+
$uiComponent = $layout->getBlock($namespace);
58+
$response = $uiComponent instanceof UiComponent ? $uiComponent->toHtml() : '';
59+
}
3360

3461
if ($buttons) {
35-
$actionsToolbar = $this->_view->getLayout()->getBlock(ActionPool::ACTIONS_PAGE_TOOLBAR);
62+
$actionsToolbar = $layout->getBlock(ActionPool::ACTIONS_PAGE_TOOLBAR);
3663
$response .= $actionsToolbar instanceof Template ? $actionsToolbar->toHtml() : '';
3764
}
3865

3966
$this->_response->appendBody($response);
4067
}
68+
69+
/**
70+
* Optionally validate ACL resource of components.
71+
*
72+
* @param mixed $dataProviderConfigData
73+
* @return bool
74+
*/
75+
private function validateAclResource($dataProviderConfigData)
76+
{
77+
$aclResource = isset($dataProviderConfigData['arguments']['data']['acl'])
78+
? $dataProviderConfigData['arguments']['data']['acl']
79+
: false;
80+
if ($aclResource !== false && !$this->_authorization->isAllowed($aclResource)
81+
) {
82+
if (!$this->_request->isAjax()) {
83+
$this->_redirect('admin/denied');
84+
}
85+
return false;
86+
}
87+
88+
return true;
89+
}
4190
}

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

Lines changed: 168 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,21 @@
55
*/
66
namespace Magento\Ui\Test\Unit\Controller\Adminhtml\Index\Render;
77

8+
use Magento\Backend\App\Action\Context;
9+
use Magento\Framework\App\Request\Http;
10+
use Magento\Framework\App\ViewInterface;
11+
use Magento\Framework\AuthorizationInterface;
12+
use Magento\Framework\HTTP\PhpEnvironment\Response;
13+
use Magento\Framework\View\Element\Template;
14+
use Magento\Framework\View\Element\UiComponent\Config\ManagerInterface;
15+
use Magento\Framework\View\Element\UiComponentFactory;
16+
use Magento\Framework\View\LayoutInterface;
17+
use Magento\Ui\Component\Wrapper\UiComponent;
818
use Magento\Ui\Controller\Adminhtml\Index\Render\Handle;
919

20+
/**
21+
* Test for Magento\Ui\Controller\Adminhtml\Index\Render\Handle class.
22+
*/
1023
class HandleTest extends \PHPUnit_Framework_TestCase
1124
{
1225
/**
@@ -39,62 +52,181 @@ class HandleTest extends \PHPUnit_Framework_TestCase
3952
*/
4053
protected $controller;
4154

55+
/**
56+
* @var ManagerInterface|\PHPUnit_Framework_MockObject_MockObject
57+
*/
58+
private $uiComponentManagerMock;
59+
60+
/**
61+
* @var UiComponent|\PHPUnit_Framework_MockObject_MockObject
62+
*/
63+
private $uiComponentMock;
64+
65+
/**
66+
* @var AuthorizationInterface|\PHPUnit_Framework_MockObject_MockObject
67+
*/
68+
private $authorizationMock;
69+
70+
/**
71+
* @var LayoutInterface|\PHPUnit_Framework_MockObject_MockObject
72+
*/
73+
private $layoutMock;
74+
75+
/**
76+
* @inheritdoc
77+
*/
4278
public function setUp()
4379
{
44-
$this->contextMock = $this->getMock('Magento\Backend\App\Action\Context', [], [], '', false);
45-
$this->componentFactoryMock = $this->getMock(
46-
'Magento\Framework\View\Element\UiComponentFactory',
47-
[],
48-
[],
49-
'',
50-
false
51-
);
52-
53-
$this->requestMock = $this->getMock('Magento\Framework\App\RequestInterface');
80+
$this->contextMock = $this->getMockBuilder(Context::class)
81+
->disableOriginalConstructor()
82+
->getMock();
83+
$this->componentFactoryMock = $this->getMockBuilder(UiComponentFactory::class)
84+
->disableOriginalConstructor()
85+
->getMock();
86+
$this->requestMock = $this->getMockBuilder(Http::class)
87+
->disableOriginalConstructor()
88+
->getMock();
5489
$this->contextMock->expects($this->atLeastOnce())->method('getRequest')->willReturn($this->requestMock);
55-
56-
$this->responseMock = $this->getMock('Magento\Framework\HTTP\PhpEnvironment\Response', [], [], '', false);
90+
$this->responseMock = $this->getMockBuilder(Response::class)
91+
->disableOriginalConstructor()
92+
->getMock();
5793
$this->contextMock->expects($this->atLeastOnce())->method('getResponse')->willReturn($this->responseMock);
58-
59-
$this->viewMock = $this->getMock('Magento\Framework\App\ViewInterface');
94+
$this->authorizationMock = $this->getMock(AuthorizationInterface::class);
95+
$this->viewMock = $this->getMockBuilder(ViewInterface::class)
96+
->disableOriginalConstructor()
97+
->getMock();
6098
$this->contextMock->expects($this->atLeastOnce())->method('getView')->willReturn($this->viewMock);
61-
62-
$this->controller = new Handle($this->contextMock, $this->componentFactoryMock);
99+
$this->contextMock->expects($this->atLeastOnce())
100+
->method('getAuthorization')
101+
->willReturn($this->authorizationMock);
102+
$this->uiComponentManagerMock = $this->getMockForAbstractClass(ManagerInterface::class);
103+
$this->uiComponentMock = $this->getMockBuilder(UiComponent::class)
104+
->disableOriginalConstructor()
105+
->getMock();
106+
$this->layoutMock = $this->getMockBuilder(LayoutInterface::class)
107+
->disableOriginalConstructor()
108+
->getMockForAbstractClass();
109+
110+
$this->controller = new Handle($this->contextMock, $this->componentFactoryMock, $this->uiComponentManagerMock);
63111

64112
}
65113

114+
/**
115+
* @return void
116+
*/
66117
public function testExecuteNoButtons()
67118
{
68-
$result = '';
69-
$this->requestMock->expects($this->exactly(3))->method('getParam')->willReturn($result);
119+
$isButtonExist = false;
120+
$isAllowed = true;
121+
$result = 'content';
70122

71-
$this->viewMock->expects($this->once())
72-
->method('loadLayout')
73-
->with(['default', $result], true, true, false);
74-
$layoutMock = $this->getMock('\Magento\Framework\View\LayoutInterface');
75-
$this->viewMock->expects($this->once())->method('getLayout')->willReturn($layoutMock);
123+
$this->prepareLayoutData($isButtonExist, $isAllowed);
76124

77-
$layoutMock->expects($this->once())->method('getBlock');
125+
$this->layoutMock->expects($this->once())
126+
->method('getBlock')
127+
->with('customer_listing')
128+
->willReturn($this->uiComponentMock);
129+
$this->uiComponentMock->expects($this->once())->method('toHtml')->willReturn($result);
130+
$this->responseMock->expects($this->once())->method('appendBody')->with($result);
78131

79-
$this->responseMock->expects($this->once())->method('appendBody')->with('');
80132
$this->controller->execute();
81133
}
82134

83-
public function testExecute()
135+
/**
136+
* @return void
137+
*/
138+
public function testExecuteWithButtons()
84139
{
85-
$result = 'SomeRequestParam';
86-
$this->requestMock->expects($this->exactly(3))->method('getParam')->willReturn($result);
140+
$isButtonExist = true;
141+
$isAllowed = true;
142+
$uiContent = 'content';
143+
$buttonContent = 'button';
144+
$templateMock = $this->getMockBuilder(Template::class)
145+
->disableOriginalConstructor()
146+
->getMock();
147+
148+
$this->prepareLayoutData($isButtonExist, $isAllowed);
149+
150+
$this->layoutMock->expects($this->at(0))
151+
->method('getBlock')
152+
->with('customer_listing')
153+
->willReturn($this->uiComponentMock);
154+
$this->uiComponentMock->expects($this->once())->method('toHtml')->willReturn($uiContent);
155+
$this->layoutMock->expects($this->at(1))
156+
->method('getBlock')
157+
->with('page.actions.toolbar')
158+
->willReturn($templateMock);
159+
$templateMock->expects($this->once())->method('toHtml')->willReturn($buttonContent);
160+
$this->responseMock->expects($this->once())->method('appendBody')->with($uiContent . $buttonContent);
87161

88-
$this->viewMock->expects($this->once())
89-
->method('loadLayout')
90-
->with(['default', $result], true, true, false);
91-
92-
$layoutMock = $this->getMock('\Magento\Framework\View\LayoutInterface');
93-
$this->viewMock->expects($this->exactly(2))->method('getLayout')->willReturn($layoutMock);
94-
95-
$layoutMock->expects($this->exactly(2))->method('getBlock');
162+
$this->controller->execute();
163+
}
96164

165+
/**
166+
* @return void
167+
*/
168+
public function testExecuteWithoutPermissions()
169+
{
170+
$isButtonExist = false;
171+
$isAllowed = false;
172+
173+
$this->prepareLayoutData($isButtonExist, $isAllowed);
174+
175+
$this->requestMock->expects($this->once())
176+
->method('isAjax')
177+
->willReturn(true);
178+
$this->layoutMock->expects($this->never())
179+
->method('getBlock')
180+
->willReturn($this->uiComponentMock);
181+
$this->uiComponentMock->expects($this->never())->method('toHtml');
97182
$this->responseMock->expects($this->once())->method('appendBody')->with('');
183+
98184
$this->controller->execute();
99185
}
186+
187+
/**
188+
* @param bool $isButtonExist
189+
* @param bool $isAllowed
190+
* @return void
191+
*/
192+
private function prepareLayoutData($isButtonExist, $isAllowed)
193+
{
194+
$aclResource = 'Magento_Customer::manage';
195+
$handle = 'customer_index_index';
196+
$namespace = 'customer_listing';
197+
$componentConfig = [
198+
$namespace => [
199+
'arguments' => [
200+
'data' => [
201+
'acl' => $aclResource,
202+
],
203+
],
204+
],
205+
];
206+
207+
$this->requestMock->expects($this->at(0))
208+
->method('getParam')
209+
->with('handle', null)
210+
->willReturn($handle);
211+
$this->requestMock->expects($this->at(1))
212+
->method('getParam')
213+
->with('namespace', null)
214+
->willReturn($namespace);
215+
$this->requestMock->expects($this->at(2))
216+
->method('getParam')
217+
->with('buttons', false)
218+
->willReturn($isButtonExist);
219+
$this->viewMock->expects($this->once())
220+
->method('loadLayout')
221+
->with(['default', $handle], true, true, false);
222+
$this->viewMock->expects($this->once())->method('getLayout')->willReturn($this->layoutMock);
223+
$this->uiComponentManagerMock->expects($this->once())
224+
->method('getData')
225+
->with($namespace)
226+
->willReturn($componentConfig);
227+
$this->authorizationMock->expects($this->once())
228+
->method('isAllowed')
229+
->with($aclResource)
230+
->willReturn($isAllowed);
231+
}
100232
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Ui\Controller\Adminhtml\Index\Renderer;
8+
9+
use Magento\TestFramework\Helper\Bootstrap;
10+
use Magento\Framework\AuthorizationInterface;
11+
12+
/**
13+
* Test for Magento\Ui\Controller\Adminhtml\Index\Render\Handle class.
14+
* @magentoAppArea adminhtml
15+
*/
16+
class HandleTest extends \Magento\TestFramework\TestCase\AbstractBackendController
17+
{
18+
/**
19+
* @return void
20+
* @magentoDataFixture Magento/Customer/_files/customer.php
21+
*/
22+
public function testExecuteWhenUserDoesNotHavePermission()
23+
{
24+
Bootstrap::getObjectManager()->configure([
25+
'preferences' => [
26+
AuthorizationInterface::class => \Magento\Ui\Model\AuthorizationMock::class,
27+
],
28+
]);
29+
$this->getRequest()->setParam('handle', 'customer_index_index');
30+
$this->getRequest()->setParam('namespace', 'customer_listing');
31+
$this->getRequest()->setParam('sorting%5Bfield%5D', 'entity_id');
32+
$this->getRequest()->setParam('paging%5BpageSize%5D', 20);
33+
$this->getRequest()->setParam('isAjax', 1);
34+
$this->dispatch('backend/mui/index/render_handle');
35+
$output = $this->getResponse()->getBody();
36+
37+
$this->assertEmpty($output, 'The acl restriction wasn\'t applied properly');
38+
}
39+
40+
/**
41+
* @return void
42+
* @magentoDataFixture Magento/Customer/_files/customer.php
43+
*/
44+
public function testExecuteWhenUserHasPermission()
45+
{
46+
$this->getRequest()->setParam('handle', 'customer_index_index');
47+
$this->getRequest()->setParam('namespace', 'customer_listing');
48+
$this->getRequest()->setParam('sorting%5Bfield%5D', 'entity_id');
49+
$this->getRequest()->setParam('paging%5BpageSize%5D', 20);
50+
$this->getRequest()->setParam('isAjax', 1);
51+
$this->dispatch('backend/mui/index/render_handle');
52+
$output = $this->getResponse()->getBody();
53+
54+
$this->assertNotEmpty($output, 'The acl restriction wasn\'t applied properly');
55+
}
56+
}

0 commit comments

Comments
 (0)