Skip to content

Commit c1bc36e

Browse files
committed
ACP2E-2969: REST API unable to make requests with slash (/) in SKU when using Oauth1
1 parent c38cf74 commit c1bc36e

File tree

3 files changed

+157
-100
lines changed
  • dev/tests/api-functional/framework/Magento/TestFramework/Authentication/Rest/OauthClient
  • lib/internal/Magento/Framework/Oauth/Helper

3 files changed

+157
-100
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ function ($carry, $item) {
4949

5050
return $this->helper->sign(
5151
$signatureData,
52-
'SHA256',
52+
$this->algorithm,
5353
$this->credentials->getConsumerSecret(),
5454
$this->tokenSecret,
5555
$method,
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?php
2+
3+
namespace Magento\Framework\Oauth\Helper\Signature;
4+
5+
use Laminas\Crypt\Hmac as HMACEncryption;
6+
7+
class Hmac256
8+
{
9+
/**
10+
* Sign a request
11+
*
12+
* @param array $params
13+
* @param string $signatureMethod
14+
* @param string $consumerSecret
15+
* @param string|null $tokenSecret
16+
* @param mixed $method
17+
* @param mixed $url
18+
* @return string
19+
*/
20+
public function sign(
21+
array $params,
22+
string $signatureMethod,
23+
string $consumerSecret,
24+
?string $tokenSecret = null,
25+
?string $method = null,
26+
?string $url = null
27+
): string {
28+
unset($params['oauth_signature']);
29+
30+
$binaryHash = HMACEncryption::compute(
31+
$this->assembleKey($consumerSecret, $tokenSecret),
32+
$signatureMethod,
33+
$this->getBaseSignatureString($params, $method, $url),
34+
HMACEncryption::OUTPUT_BINARY
35+
);
36+
37+
return base64_encode($binaryHash);
38+
}
39+
40+
/**
41+
* Assemble key from consumer and token secrets
42+
*
43+
* @param string $consumerSecret
44+
* @param string|null $tokenSecret
45+
* @return string
46+
*/
47+
private function assembleKey(string $consumerSecret, ?string $tokenSecret): string
48+
{
49+
$parts = [$consumerSecret];
50+
if ($tokenSecret !== null) {
51+
$parts[] = $tokenSecret;
52+
}
53+
foreach ($parts as $key => $secret) {
54+
$parts[$key] = $this->urlEncode($secret);
55+
}
56+
57+
return implode('&', $parts);
58+
}
59+
60+
/**
61+
* Get base signature string
62+
*
63+
* @param array $params
64+
* @param null|string $method
65+
* @param null|string $url
66+
* @return string
67+
*/
68+
private function getBaseSignatureString(array $params, $method = null, $url = null): string
69+
{
70+
$encodedParams = [];
71+
foreach ($params as $key => $value) {
72+
$encodedParams[$this->urlEncode($key)] =
73+
$this->urlEncode($value);
74+
}
75+
$baseStrings = [];
76+
if (isset($method)) {
77+
$baseStrings[] = strtoupper($method);
78+
}
79+
if (isset($url)) {
80+
$baseStrings[] = $this->urlEncode($url);
81+
}
82+
if (isset($encodedParams['oauth_signature'])) {
83+
unset($encodedParams['oauth_signature']);
84+
}
85+
$baseStrings[] = $this->urlEncode(
86+
$this->toByteValueOrderedQueryString($encodedParams)
87+
);
88+
89+
return implode('&', $baseStrings);
90+
}
91+
92+
/**
93+
* Transform an array to a byte value ordered query string
94+
*
95+
* @param array $params
96+
* @return string
97+
*/
98+
private function toByteValueOrderedQueryString(array $params): string
99+
{
100+
$return = [];
101+
uksort($params, 'strnatcmp');
102+
foreach ($params as $key => $value) {
103+
if (is_array($value)) {
104+
natsort($value);
105+
foreach ($value as $keyduplicate) {
106+
$return[] = $key . '=' . $keyduplicate;
107+
}
108+
} else {
109+
$return[] = $key . '=' . $value;
110+
}
111+
}
112+
return implode('&', $return);
113+
}
114+
115+
/**
116+
* URL encode a value
117+
*
118+
* @param string $value
119+
* @return string
120+
*/
121+
private function urlEncode(string $value): string
122+
{
123+
$encoded = rawurlencode($value);
124+
return str_replace('%7E', '~', $encoded);
125+
}
126+
}

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

Lines changed: 30 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,19 @@
77

88
namespace Magento\Framework\Oauth\Helper;
99

10-
use Laminas\Crypt\Hmac as HMACEncryption;
10+
use Laminas\OAuth\Http\Utility as LaminasUtility;
11+
use Magento\Framework\Oauth\Helper\Signature\Hmac256;
1112

1213
class Utility
1314
{
15+
/**
16+
* @param LaminasUtility $httpUtility
17+
* @param Hmac256 $hmac256
18+
*/
19+
public function __construct(private readonly LaminasUtility $httpUtility, private readonly Hmac256 $hmac256)
20+
{
21+
}
22+
1423
/**
1524
* Generate signature string
1625
*
@@ -30,17 +39,26 @@ public function sign(
3039
?string $method = null,
3140
?string $url = null
3241
): string {
33-
unset($params['oauth_signature']);
42+
if ($this->isHmac256($signatureMethod)) {
43+
return $this->hmac256->sign($params, 'sha256', $consumerSecret, $tokenSecret, $method, $url);
44+
} else {
45+
return $this->httpUtility->sign($params, $signatureMethod, $consumerSecret, $tokenSecret, $method, $url);
46+
}
47+
}
3448

35-
$parts = explode('-', $signatureMethod);
36-
$binaryHash = HMACEncryption::compute(
37-
$this->assembleKey($consumerSecret, $tokenSecret),
38-
count($parts) > 1 ? $parts[1] : $signatureMethod,
39-
$this->getBaseSignatureString($params, $method, $url),
40-
HMACEncryption::OUTPUT_BINARY
41-
);
49+
/**
50+
* Check if signature method is HMAC256
51+
*
52+
* @param string $signatureMethod
53+
* @return bool
54+
*/
55+
private function isHmac256(string $signatureMethod): bool
56+
{
57+
if (strtoupper(preg_replace( '/[\W]/', '', $signatureMethod)) === 'HMAC256') {
58+
return true;
59+
}
4260

43-
return base64_encode($binaryHash);
61+
return false;
4462
}
4563

4664
/**
@@ -59,97 +77,10 @@ public function toAuthorizationHeader(array $params, bool $excludeCustomParams =
5977
continue;
6078
}
6179
}
62-
$headerValue[] = $this->urlEncode((string)$key)
80+
$headerValue[] = $this->httpUtility::urlEncode((string)$key)
6381
. '="'
64-
. $this->urlEncode((string)$value) . '"';
82+
. $this->httpUtility::urlEncode((string)$value) . '"';
6583
}
6684
return 'OAuth ' . implode(",", $headerValue);
6785
}
68-
69-
/**
70-
* Assemble key from consumer and token secrets
71-
*
72-
* @param string $consumerSecret
73-
* @param string|null $tokenSecret
74-
* @return string
75-
*/
76-
private function assembleKey(string $consumerSecret, ?string $tokenSecret): string
77-
{
78-
$parts = [$consumerSecret];
79-
if ($tokenSecret !== null) {
80-
$parts[] = $tokenSecret;
81-
}
82-
foreach ($parts as $key => $secret) {
83-
$parts[$key] = $this->urlEncode($secret);
84-
}
85-
86-
return implode('&', $parts);
87-
}
88-
89-
/**
90-
* Get base signature string
91-
*
92-
* @param array $params
93-
* @param null|string $method
94-
* @param null|string $url
95-
* @return string
96-
*/
97-
private function getBaseSignatureString(array $params, $method = null, $url = null): string
98-
{
99-
$encodedParams = [];
100-
foreach ($params as $key => $value) {
101-
$encodedParams[$this->urlEncode($key)] =
102-
$this->urlEncode($value);
103-
}
104-
$baseStrings = [];
105-
if (isset($method)) {
106-
$baseStrings[] = strtoupper($method);
107-
}
108-
if (isset($url)) {
109-
$baseStrings[] = $this->urlEncode($url);
110-
}
111-
if (isset($encodedParams['oauth_signature'])) {
112-
unset($encodedParams['oauth_signature']);
113-
}
114-
$baseStrings[] = $this->urlEncode(
115-
$this->toByteValueOrderedQueryString($encodedParams)
116-
);
117-
118-
return implode('&', $baseStrings);
119-
}
120-
121-
/**
122-
* Transform an array to a byte value ordered query string
123-
*
124-
* @param array $params
125-
* @return string
126-
*/
127-
private function toByteValueOrderedQueryString(array $params): string
128-
{
129-
$return = [];
130-
uksort($params, 'strnatcmp');
131-
foreach ($params as $key => $value) {
132-
if (is_array($value)) {
133-
natsort($value);
134-
foreach ($value as $keyduplicate) {
135-
$return[] = $key . '=' . $keyduplicate;
136-
}
137-
} else {
138-
$return[] = $key . '=' . $value;
139-
}
140-
}
141-
return implode('&', $return);
142-
}
143-
144-
/**
145-
* URL encode a value
146-
*
147-
* @param string $value
148-
* @return string
149-
*/
150-
private function urlEncode(string $value): string
151-
{
152-
$encoded = rawurlencode($value);
153-
return str_replace('%7E', '~', $encoded);
154-
}
15586
}

0 commit comments

Comments
 (0)