Skip to content

Commit 3a8eb28

Browse files
committed
Merge branch 'ACP2E-2969' of https://github.com/adobe-commerce-tier-4/magento2ce into PR-2024-07-11
2 parents 4be1585 + 3c97348 commit 3a8eb28

File tree

9 files changed

+313
-75
lines changed

9 files changed

+313
-75
lines changed

app/code/Magento/Integration/Test/Unit/Oauth/OauthTest.php

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace Magento\Integration\Test\Unit\Oauth;
99

10-
use Laminas\OAuth\Http\Utility;
10+
use Magento\Framework\Oauth\Helper\Utility;
1111
use Magento\Framework\DataObject;
1212
use Magento\Framework\Math\Random;
1313
use Magento\Framework\Oauth\Helper\Oauth;
@@ -55,8 +55,8 @@ class OauthTest extends TestCase
5555
/** @var \Magento\Framework\Oauth\Oauth */
5656
private $_oauth;
5757

58-
/** @var \Zend_Oauth_Http_Utility */
59-
private $_httpUtilityMock;
58+
/** @var Utility */
59+
private $utility;
6060

6161
/** @var DateTime */
6262
private $_dateMock;
@@ -160,9 +160,7 @@ protected function setUp(): void
160160
$this->_oauthHelperMock = $this->getMockBuilder(Oauth::class)
161161
->setConstructorArgs([new Random()])
162162
->getMock();
163-
$this->_httpUtilityMock = $this->getMockBuilder(Utility::class)
164-
->onlyMethods(['sign'])
165-
->getMock();
163+
$this->utility = $this->createMock(Utility::class);
166164
$this->_dateMock = $this->getMockBuilder(DateTime::class)
167165
->disableOriginalConstructor()
168166
->getMock();
@@ -190,7 +188,7 @@ protected function setUp(): void
190188
$this->_oauthHelperMock,
191189
$nonceGenerator,
192190
$tokenProvider,
193-
$this->_httpUtilityMock
191+
$this->utility
194192
);
195193
$this->_oauthToken = $this->_generateRandomString(Oauth::LENGTH_TOKEN);
196194
$this->_oauthSecret = $this->_generateRandomString(Oauth::LENGTH_TOKEN_SECRET);
@@ -199,17 +197,6 @@ protected function setUp(): void
199197
);
200198
}
201199

202-
protected function tearDown(): void
203-
{
204-
unset($this->_consumerFactory);
205-
unset($this->_nonceFactory);
206-
unset($this->_tokenFactory);
207-
unset($this->_oauthHelperMock);
208-
unset($this->_httpUtilityMock);
209-
unset($this->_dateMock);
210-
unset($this->_oauth);
211-
}
212-
213200
/**
214201
* @param array $amendments
215202
* @return array
@@ -477,7 +464,7 @@ public function testGetRequestTokenTokenRejected()
477464
$this->_setupToken(false);
478465

479466
$signature = 'valid_signature';
480-
$this->_httpUtilityMock->expects($this->any())->method('sign')->willReturn($signature);
467+
$this->utility->expects($this->any())->method('sign')->willReturn($signature);
481468

482469
$this->_oauth->getRequestToken(
483470
$this->_getRequestTokenParams(['oauth_signature' => $signature]),
@@ -498,7 +485,7 @@ public function testGetRequestTokenTokenRejectedByType()
498485
// wrong type
499486

500487
$signature = 'valid_signature';
501-
$this->_httpUtilityMock->expects($this->any())->method('sign')->willReturn($signature);
488+
$this->utility->expects($this->any())->method('sign')->willReturn($signature);
502489

503490
$this->_oauth->getRequestToken(
504491
$this->_getRequestTokenParams(['oauth_signature' => $signature]),
@@ -548,7 +535,7 @@ public function testGetRequestToken()
548535
$this->_setupToken();
549536

550537
$signature = 'valid_signature';
551-
$this->_httpUtilityMock->expects($this->any())->method('sign')->willReturn($signature);
538+
$this->utility->expects($this->any())->method('sign')->willReturn($signature);
552539

553540
$requestToken = $this->_oauth->getRequestToken(
554541
$this->_getRequestTokenParams(['oauth_signature' => $signature]),
@@ -802,7 +789,13 @@ public function testValidateAccessToken()
802789
public function testBuildAuthorizationHeader()
803790
{
804791
$signature = 'valid_signature';
805-
$this->_httpUtilityMock->expects($this->any())->method('sign')->willReturn($signature);
792+
$this->utility->expects($this->once())->method('sign')->willReturn($signature);
793+
$this->utility->expects($this->once())
794+
->method('toAuthorizationHeader')
795+
->willReturn('OAuth oauth_nonce="tyukmnjhgfdcvxstyuioplkmnhtfvert",oauth_timestamp="1657789046",' .
796+
'oauth_version="1.0",oauth_consumer_key="edf957ef88492f0a32eb7e1731e85da2",' .
797+
'oauth_consumer_secret="asdawwewefrtyh2f0a32eb7e1731e85d",oauth_token="7c0709f789e1f38a17aa4b9a28e1b06c",' .
798+
'oauth_token_secret="a6agsfrsfgsrjjjjyy487939244ssggg",oauth_signature="valid_signature"');
806799

807800
$this->_setupConsumer(false);
808801
$this->_oauthHelperMock->expects(

dev/tests/api-functional/framework/Magento/TestFramework/Authentication/Rest/OauthClient.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77
namespace Magento\TestFramework\Authentication\Rest;
88

9+
use Magento\Framework\Oauth\Helper\Utility;
910
use Magento\TestFramework\Helper\Bootstrap;
11+
use Magento\TestFramework\ObjectManager;
1012
use OAuth\Common\Consumer\Credentials;
1113
use OAuth\Common\Http\Client\ClientInterface;
1214
use OAuth\Common\Http\Exception\TokenResponseException;
@@ -17,6 +19,8 @@
1719
use OAuth\OAuth1\Signature\SignatureInterface;
1820
use OAuth\OAuth1\Token\StdOAuth1Token;
1921
use OAuth\OAuth1\Token\TokenInterface;
22+
use Laminas\OAuth\Http\Utility as HTTPUtility;
23+
use Magento\Framework\Oauth\Helper\Signature\HmacFactory;
2024

2125
/**
2226
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -37,13 +41,15 @@ class OauthClient extends AbstractService
3741
* @param TokenStorageInterface|null $storage
3842
* @param SignatureInterface|null $signature
3943
* @param UriInterface|null $baseApiUri
44+
* @param Utility|null $helper
4045
*/
4146
public function __construct(
4247
Credentials $credentials,
4348
ClientInterface $httpClient = null,
4449
TokenStorageInterface $storage = null,
4550
SignatureInterface $signature = null,
46-
UriInterface $baseApiUri = null
51+
UriInterface $baseApiUri = null,
52+
Utility $helper = null
4753
) {
4854
if (!isset($httpClient)) {
4955
$httpClient = new \Magento\TestFramework\Authentication\Rest\CurlClient();
@@ -52,8 +58,12 @@ public function __construct(
5258
if (!isset($storage)) {
5359
$storage = new \OAuth\Common\Storage\Memory();
5460
}
61+
if (!isset($helper)) {
62+
/** @phpstan-ignore-next-line */
63+
$helper = new Utility(new HTTPUtility(), new HmacFactory(ObjectManager::getInstance()));
64+
}
5565
if (!isset($signature)) {
56-
$signature = new \Magento\TestFramework\Authentication\Rest\OauthClient\Signature($credentials);
66+
$signature = new \Magento\TestFramework\Authentication\Rest\OauthClient\Signature($helper, $credentials);
5767
}
5868
parent::__construct($credentials, $httpClient, $storage, $signature, $baseApiUri);
5969
}

dev/tests/api-functional/framework/Magento/TestFramework/Authentication/Rest/OauthClient/Signature.php

Lines changed: 22 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,29 @@
66

77
namespace Magento\TestFramework\Authentication\Rest\OauthClient;
88

9+
use OAuth\Common\Consumer\CredentialsInterface;
910
use OAuth\Common\Http\Uri\UriInterface;
11+
use Magento\Framework\Oauth\Helper\Utility;
1012

1113
/**
1214
* Signature class for Magento REST API.
1315
*/
1416
class Signature extends \OAuth\OAuth1\Signature\Signature
1517
{
18+
/**
19+
* @param Utility $helper
20+
* @param CredentialsInterface $credentials
21+
*/
22+
public function __construct(private readonly Utility $helper, CredentialsInterface $credentials)
23+
{
24+
parent::__construct($credentials);
25+
}
26+
1627
/**
1728
* @inheritDoc
1829
*
19-
* In addition to the original method, allows array parameters for filters.
30+
* In addition to the original method, allows array parameters for filters
31+
* and matches validation signature algorithm
2032
*/
2133
public function getSignature(UriInterface $uri, array $params, $method = 'POST')
2234
{
@@ -32,37 +44,16 @@ function ($carry, $item) {
3244

3345
$signatureData = [];
3446
foreach (array_merge($queryStringData, $params) as $key => $value) {
35-
$signatureData[rawurlencode($key)] = rawurlencode($value);
36-
}
37-
38-
ksort($signatureData);
39-
40-
// determine base uri
41-
$baseUri = $uri->getScheme() . '://' . $uri->getRawAuthority();
42-
43-
if ('/' == $uri->getPath()) {
44-
$baseUri .= $uri->hasExplicitTrailingHostSlash() ? '/' : '';
45-
} else {
46-
$baseUri .= $uri->getPath();
47+
$signatureData[rawurldecode($key)] = rawurlencode($value);
4748
}
4849

49-
$baseString = strtoupper($method) . '&';
50-
$baseString .= rawurlencode($baseUri) . '&';
51-
$baseString .= rawurlencode($this->buildSignatureDataString($signatureData));
52-
53-
return base64_encode($this->hash($baseString));
54-
}
55-
56-
/**
57-
* @inheritDoc
58-
*/
59-
protected function hash($data)
60-
{
61-
switch (strtoupper($this->algorithm)) {
62-
case 'HMAC-SHA256':
63-
return hash_hmac('sha256', $data, $this->getSigningKey(), true);
64-
default:
65-
return parent::hash($data);
66-
}
50+
return $this->helper->sign(
51+
$signatureData,
52+
$this->algorithm,
53+
$this->credentials->getConsumerSecret(),
54+
$this->tokenSecret,
55+
$method,
56+
(string) $uri
57+
);
6758
}
6859
}

lib/internal/Magento/Framework/Oauth/Helper/Request.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ public function prepareRequest($httpRequest)
4343
$httpRequest->getContent(),
4444
$this->getRequestUrl($httpRequest)
4545
);
46+
foreach ($oauthParams as $key => $value) {
47+
if ($key !== 'oauth_signature') {
48+
$oauthParams[$key] = rawurlencode($value);
49+
}
50+
}
4651
return $oauthParams;
4752
}
4853

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
/**
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Framework\Oauth\Helper\Signature;
9+
10+
use Laminas\Uri;
11+
use Laminas\OAuth\Signature\Hmac as HmacSignature;
12+
use Magento\Framework\Oauth\Helper\Uri\Http;
13+
14+
class Hmac extends HmacSignature
15+
{
16+
/**
17+
* @inheritDoc
18+
*/
19+
public function normaliseBaseSignatureUrl($url): string
20+
{
21+
$registeredHttpScheme = Uri\UriFactory::getRegisteredSchemeClass('http');
22+
$registeredHttpsScheme = Uri\UriFactory::getRegisteredSchemeClass('https');
23+
Uri\UriFactory::registerScheme('http', Http::class);
24+
Uri\UriFactory::registerScheme('https', Http::class);
25+
$uri = Uri\UriFactory::factory($url);
26+
Uri\UriFactory::registerScheme('http', $registeredHttpScheme);
27+
Uri\UriFactory::registerScheme('https', $registeredHttpsScheme);
28+
$uri->normalize();
29+
if ($uri->getScheme() == 'http' && $uri->getPort() == '80') {
30+
$uri->setPort('');
31+
} elseif ($uri->getScheme() == 'https' && $uri->getPort() == '443') {
32+
$uri->setPort('');
33+
} elseif (! in_array($uri->getScheme(), ['http', 'https'])) {
34+
throw new \InvalidArgumentException('Invalid URL provided; must be an HTTP or HTTPS scheme');
35+
}
36+
$uri->setQuery('');
37+
$uri->setFragment('');
38+
return $uri->toString();
39+
}
40+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
/**
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Framework\Oauth\Helper\Uri;
9+
10+
use Laminas\Uri\Http as LaminasHttp;
11+
12+
class Http extends LaminasHttp
13+
{
14+
/**
15+
* @inheritDoc
16+
*/
17+
protected static function normalizePath($path): string
18+
{
19+
return self::encodePath(
20+
self::decodeUrlEncodedChars(
21+
self::removePathDotSegments($path),
22+
'/[' . self::CHAR_UNRESERVED . ':@&=\+\$,;%]/'
23+
)
24+
);
25+
}
26+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
/**
3+
* Copyright 2024 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Framework\Oauth\Helper;
9+
10+
use Laminas\OAuth\Http\Utility as LaminasUtility;
11+
use Magento\Framework\Oauth\Helper\Signature\HmacFactory;
12+
13+
class Utility
14+
{
15+
/**
16+
* @param LaminasUtility $httpUtility
17+
* @param HmacFactory $hmacFactory
18+
*/
19+
public function __construct(private readonly LaminasUtility $httpUtility, private readonly HmacFactory $hmacFactory)
20+
{
21+
}
22+
23+
/**
24+
* Generate signature string
25+
*
26+
* @param array $params
27+
* @param string $signatureMethod
28+
* @param string $consumerSecret
29+
* @param string|null $tokenSecret
30+
* @param string|null $method
31+
* @param string|null $url
32+
* @return string
33+
*/
34+
public function sign(
35+
array $params,
36+
string $signatureMethod,
37+
string $consumerSecret,
38+
?string $tokenSecret = null,
39+
?string $method = null,
40+
?string $url = null
41+
): string {
42+
if ($this->isHmac256($signatureMethod)) {
43+
$hmac = $this->hmacFactory->create(
44+
['consumerSecret' => $consumerSecret,
45+
'tokenSecret' => $tokenSecret,
46+
'hashAlgo' => 'sha256'
47+
]
48+
);
49+
50+
return $hmac->sign($params, $method, $url);
51+
} else {
52+
return $this->httpUtility->sign($params, $signatureMethod, $consumerSecret, $tokenSecret, $method, $url);
53+
}
54+
}
55+
56+
/**
57+
* Check if signature method is HMAC-SHA256
58+
*
59+
* @param string $signatureMethod
60+
* @return bool
61+
*/
62+
private function isHmac256(string $signatureMethod): bool
63+
{
64+
if (strtoupper(preg_replace('/[\W]/', '', $signatureMethod)) === 'HMACSHA256') {
65+
return true;
66+
}
67+
68+
return false;
69+
}
70+
71+
/**
72+
* Cast to authorization header
73+
*
74+
* @param array $params
75+
* @param string|null $realm
76+
* @param bool $excludeCustomParams
77+
*/
78+
public function toAuthorizationHeader(array $params, ?string $realm = null, bool $excludeCustomParams = true)
79+
{
80+
$authorizationHeader = $this->httpUtility->toAuthorizationHeader($params, $realm, $excludeCustomParams);
81+
return str_replace('realm="",', '', $authorizationHeader);
82+
}
83+
}

0 commit comments

Comments
 (0)