Skip to content

Commit 6015bdf

Browse files
authored
Merge branch '2.4-develop' into issue-24635
2 parents fa25a53 + d7b0245 commit 6015bdf

File tree

23 files changed

+1384
-92
lines changed

23 files changed

+1384
-92
lines changed

app/code/Magento/Backend/App/Action/Plugin/Authentication.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,10 @@ protected function _redirectIfNeededAfterLogin(\Magento\Framework\App\RequestInt
225225

226226
// Checks, whether secret key is required for admin access or request uri is explicitly set
227227
if ($this->_url->useSecretKey()) {
228-
$requestParts = explode('/', trim($request->getRequestUri(), '/'), 2);
229-
$requestUri = $this->_url->getUrl(array_pop($requestParts));
228+
$requestParts = explode('/', trim($request->getRequestUri(), '/'), 3);
229+
$baseUrlPath = trim(parse_url($this->backendUrl->getBaseUrl(), PHP_URL_PATH), '/');
230+
$routeIndex = empty($baseUrlPath) ? 0 : 1;
231+
$requestUri = $this->_url->getUrl($requestParts[$routeIndex]);
230232
} elseif ($request) {
231233
$requestUri = $request->getRequestUri();
232234
}

app/code/Magento/Backend/Controller/Adminhtml/Auth/Login.php

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
<?php
22
/**
3-
*
43
* Copyright © Magento, Inc. All rights reserved.
54
* See COPYING.txt for license details.
65
*/
76
namespace Magento\Backend\Controller\Adminhtml\Auth;
87

8+
use Magento\Backend\App\Area\FrontNameResolver;
9+
use Magento\Backend\App\BackendAppList;
10+
use Magento\Backend\Model\UrlFactory;
911
use Magento\Framework\App\Action\HttpGetActionInterface as HttpGet;
1012
use Magento\Framework\App\Action\HttpPostActionInterface as HttpPost;
13+
use Magento\Framework\App\ObjectManager;
14+
use Magento\Framework\App\Request\Http;
1115

1216
/**
1317
* @api
@@ -20,18 +24,50 @@ class Login extends \Magento\Backend\Controller\Adminhtml\Auth implements HttpGe
2024
*/
2125
protected $resultPageFactory;
2226

27+
/**
28+
* @var FrontNameResolver
29+
*/
30+
private $frontNameResolver;
31+
32+
/**
33+
* @var BackendAppList
34+
*/
35+
private $backendAppList;
36+
37+
/**
38+
* @var UrlFactory
39+
*/
40+
private $backendUrlFactory;
41+
42+
/**
43+
* @var Http
44+
*/
45+
private $http;
46+
2347
/**
2448
* Constructor
2549
*
2650
* @param \Magento\Backend\App\Action\Context $context
2751
* @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
52+
* @param FrontNameResolver|null $frontNameResolver
53+
* @param BackendAppList|null $backendAppList
54+
* @param UrlFactory|null $backendUrlFactory
55+
* @param Http|null $http
2856
*/
2957
public function __construct(
3058
\Magento\Backend\App\Action\Context $context,
31-
\Magento\Framework\View\Result\PageFactory $resultPageFactory
59+
\Magento\Framework\View\Result\PageFactory $resultPageFactory,
60+
FrontNameResolver $frontNameResolver = null,
61+
BackendAppList $backendAppList = null,
62+
UrlFactory $backendUrlFactory = null,
63+
Http $http = null
3264
) {
3365
$this->resultPageFactory = $resultPageFactory;
3466
parent::__construct($context);
67+
$this->frontNameResolver = $frontNameResolver ?? ObjectManager::getInstance()->get(FrontNameResolver::class);
68+
$this->backendAppList = $backendAppList ?? ObjectManager::getInstance()->get(BackendAppList::class);
69+
$this->backendUrlFactory = $backendUrlFactory ?? ObjectManager::getInstance()->get(UrlFactory::class);
70+
$this->http = $http ?? ObjectManager::getInstance()->get(Http::class);
3571
}
3672

3773
/**
@@ -49,7 +85,8 @@ public function execute()
4985
}
5086

5187
$requestUrl = $this->getRequest()->getUri();
52-
if (!$requestUrl->isValid()) {
88+
89+
if (!$requestUrl->isValid() || !$this->isValidBackendUri()) {
5390
return $this->getRedirect($this->getUrl('*'));
5491
}
5592

@@ -69,4 +106,26 @@ private function getRedirect($path)
69106
$resultRedirect->setPath($path);
70107
return $resultRedirect;
71108
}
109+
110+
/**
111+
* Verify if correct backend uri requested.
112+
*
113+
* @return bool
114+
*/
115+
private function isValidBackendUri(): bool
116+
{
117+
$requestUri = $this->getRequest()->getRequestUri();
118+
$backendApp = $this->backendAppList->getCurrentApp();
119+
$baseUrl = parse_url($this->backendUrlFactory->create()->getBaseUrl(), PHP_URL_PATH);
120+
if (!$backendApp) {
121+
$backendFrontName = $this->frontNameResolver->getFrontName();
122+
} else {
123+
//In case of application authenticating through the admin login, the script name should be removed
124+
//from the path, because application has own script.
125+
$baseUrl = $this->http->getUrlNoScript($baseUrl);
126+
$backendFrontName = $backendApp->getCookiePath();
127+
}
128+
129+
return strpos($requestUri, $baseUrl . $backendFrontName) === 0;
130+
}
72131
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
9+
<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd">
11+
<entity name="AdminEnableUrlRewritesConfigData">
12+
<data key="path">web/seo/use_rewrites</data>
13+
<data key="value">1</data>
14+
</entity>
15+
<entity name="AdminDisableUrlRewritesConfigData">
16+
<data key="path">web/seo/use_rewrites</data>
17+
<data key="value">0</data>
18+
</entity>
19+
</entities>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
9+
<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
11+
<test name="AdminLoginSuccessfulWithRewritesDisabledTest">
12+
<annotations>
13+
<features value="Backend"/>
14+
<stories value="Login on the Admin Login page"/>
15+
<title
16+
value="Admin should be able to log into the Magento Admin backend successfully if url rewrites are disabled"/>
17+
<description
18+
value="Admin should be able to log into the Magento Admin backend successfully if url rewrites are disabled"/>
19+
<severity value="CRITICAL"/>
20+
<group value="example"/>
21+
<group value="login"/>
22+
</annotations>
23+
24+
<before>
25+
<magentoCLI command="config:set {{AdminDisableUrlRewritesConfigData.path}} {{AdminDisableUrlRewritesConfigData.value}}" stepKey="disableUrlRewrites"/>
26+
</before>
27+
<after>
28+
<magentoCLI command="config:set {{AdminEnableUrlRewritesConfigData.path}} {{AdminEnableUrlRewritesConfigData.value}}" stepKey="enableUrlRewrites"/>
29+
</after>
30+
31+
<actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
32+
<actionGroup ref="AssertAdminSuccessLoginActionGroup" stepKey="assertLoggedIn"/>
33+
<actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/>
34+
</test>
35+
</tests>
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
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\Backend\Test\Unit\Controller\Adminhtml\Auth;
9+
10+
use Laminas\Uri\Http;
11+
use Magento\Backend\App\Action\Context;
12+
use Magento\Backend\App\Area\FrontNameResolver;
13+
use Magento\Backend\App\BackendAppList;
14+
use Magento\Backend\Controller\Adminhtml\Auth\Login;
15+
use Magento\Backend\Helper\Data;
16+
use Magento\Backend\Model\Auth;
17+
use Magento\Backend\Model\Url;
18+
use Magento\Backend\Model\UrlFactory;
19+
use Magento\Backend\Model\View\Result\Redirect;
20+
use Magento\Framework\App\Cache\StateInterface as CacheState;
21+
use Magento\Framework\App\Cache\TypeListInterface as CacheTypeList;
22+
use Magento\Framework\App\RequestInterface as Request;
23+
use Magento\Framework\App\State;
24+
use Magento\Framework\Controller\Result\RedirectFactory;
25+
use Magento\Framework\Message\ManagerInterface as MessageManager;
26+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
27+
use Magento\Framework\View\Result\PageFactory;
28+
use PHPUnit\Framework\MockObject\MockObject;
29+
use PHPUnit\Framework\TestCase;
30+
31+
/**
32+
* Test for \Magento\Backend\Controller\Adminhtml\Auth\Login.
33+
*/
34+
class LoginTest extends TestCase
35+
{
36+
/**
37+
* @var Login
38+
*/
39+
private $controller;
40+
41+
/**
42+
* @var Redirect|MockObject
43+
*/
44+
private $redirectMock;
45+
46+
/**
47+
* @var Request|MockObject
48+
*/
49+
private $requestMock;
50+
51+
/**
52+
* @var Auth|MockObject
53+
*/
54+
private $authMock;
55+
56+
/**
57+
* @var Http|MockObject
58+
*/
59+
private $uriMock;
60+
61+
/**
62+
* @var PageFactory|MockObject
63+
*/
64+
private $resultPageFactoryMock;
65+
66+
/**
67+
* @var BackendAppList|MockObject
68+
*/
69+
private $backendAppListMock;
70+
71+
/**
72+
* @var UrlFactory|MockObject
73+
*/
74+
private $backendUrlFactoryMock;
75+
76+
/**
77+
* @var Url|MockObject
78+
*/
79+
private $backendUrlMock;
80+
81+
/**
82+
* @var FrontNameResolver|MockObject
83+
*/
84+
private $frontNameResolverMock;
85+
86+
/**
87+
* @var RedirectFactory|MockObject
88+
*/
89+
private $resultRedirectFactoryMock;
90+
91+
/**
92+
* @var Data|MockObject
93+
*/
94+
private $helperMock;
95+
96+
protected function setUp(): void
97+
{
98+
$objectManagerHelper = new ObjectManagerHelper($this);
99+
100+
$this->helperMock = $this->createMock(Data::class);
101+
$this->requestMock = $this->getMockBuilder(Request::class)
102+
->setMethods(['getUri', 'getRequestUri'])
103+
->getMockForAbstractClass();
104+
$this->redirectMock = $this->getMockBuilder(Redirect::class)
105+
->disableOriginalConstructor()
106+
->disableOriginalClone()
107+
->getMock();
108+
$this->resultRedirectFactoryMock = $this->createMock(RedirectFactory::class);
109+
$this->resultPageFactoryMock = $this->createMock(PageFactory::class);
110+
$this->authMock = $this->createMock(Auth::class);
111+
$this->backendAppListMock = $this->createMock(BackendAppList::class);
112+
$this->backendUrlMock = $this->createMock(Url::class);
113+
$this->backendUrlFactoryMock = $this->createMock(UrlFactory::class);
114+
$this->frontNameResolverMock = $this->createMock(FrontNameResolver::class);
115+
$this->uriMock = $this->createMock(Http::class);
116+
117+
$this->resultRedirectFactoryMock->expects($this->any())
118+
->method('create')
119+
->willReturn($this->redirectMock);
120+
$this->backendUrlFactoryMock->expects($this->any())
121+
->method('create')
122+
->willReturn($this->backendUrlMock);
123+
$this->requestMock->expects($this->any())
124+
->method('getUri')
125+
->willReturn($this->uriMock);
126+
127+
$contextMock = $this->getMockBuilder(Context::class)
128+
->disableOriginalConstructor()
129+
->disableOriginalClone()
130+
->getMock();
131+
132+
$contextMock->expects($this->once())
133+
->method('getResultFactory')
134+
->willReturn($this->resultRedirectFactoryMock);
135+
$contextMock->expects($this->once())
136+
->method('getRequest')
137+
->willReturn($this->requestMock);
138+
$contextMock->expects($this->once())
139+
->method('getHelper')
140+
->willReturn($this->helperMock);
141+
$contextMock->expects($this->once())
142+
->method('getResultRedirectFactory')
143+
->willReturn($this->resultRedirectFactoryMock);
144+
$contextMock->expects($this->once())
145+
->method('getAuth')
146+
->willReturn($this->authMock);
147+
148+
$this->controller = $objectManagerHelper->getObject(
149+
Login::class,
150+
[
151+
'context' => $contextMock,
152+
'resultPageFactory' => $this->resultPageFactoryMock,
153+
'backendAppList' => $this->backendAppListMock,
154+
'backendUrlFactory' => $this->backendUrlFactoryMock,
155+
'frontNameResolver' => $this->frontNameResolverMock,
156+
]
157+
);
158+
}
159+
160+
/**
161+
* Test for isValidBackendUri method.
162+
*
163+
* @param string $requestUri
164+
* @param string $baseUrl
165+
* @param string $backendFrontName
166+
* @param bool $redirect
167+
*
168+
* @dataProvider isValidBackendUriDataProvider
169+
*/
170+
public function testIsValidBackendUri(string $requestUri, string $baseUrl, string $backendFrontName, bool $redirect)
171+
{
172+
$this->uriMock->expects($this->once())->method('isValid')->willReturn(true);
173+
$this->authMock->expects($this->once())->method('isLoggedIn')->willReturn(false);
174+
$this->requestMock->expects($this->once())->method('getRequestUri')->willReturn($requestUri);
175+
$this->backendUrlMock->expects($this->once())->method('getBaseUrl')->willReturn($baseUrl);
176+
$this->frontNameResolverMock->expects($this->once())->method('getFrontName')->willReturn($backendFrontName);
177+
178+
$this->resultPageFactoryMock->expects($this->exactly($redirect ? 0 : 1))->method('create');
179+
$this->resultRedirectFactoryMock->expects($this->exactly($redirect ? 1 : 0))->method('create');
180+
181+
$this->controller->execute();
182+
}
183+
184+
/**
185+
* Data provider for testIsValidBackendUri.
186+
*
187+
* @return array[]
188+
*/
189+
public function isValidBackendUriDataProvider()
190+
{
191+
return [
192+
'Rewrites on, valid url' => ['/index.php/admin', 'http://magento2.local/', 'admin', true],
193+
'Rewrites on, invalid url' => ['/admin', 'http://magento2.local/', 'admin', false],
194+
'Rewrites off, valid url' => ['/index.php/admin', 'http://magento2.local/index.php/', 'admin', false],
195+
'Rewrites off, invalid url' => ['/admin', 'http://magento2.local/index.php/', 'admin', true],
196+
];
197+
}
198+
}

0 commit comments

Comments
 (0)