Skip to content

Commit a68fdf3

Browse files
ChristopherDosinpbtkhoa
authored andcommitted
Merge pull request #8 from Shape-and-Shift/feature/add-sw-iframe-middleware
Add Sw Iframe Middleware
2 parents 9a0bff9 + 7ffa63c commit a68fdf3

File tree

8 files changed

+139
-42
lines changed

8 files changed

+139
-42
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
### 1.3.0
2+
- Add SwAppIframeMiddleware to verify incoming requests from Iframe Shopware
3+
14
### 1.2.1
25
- Fix wrong shopId parameter
36

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ Your app is now ready to install by a Shopware application!
4747
## Usage
4848
- Context, ShopRepository auto-binding
4949
- SwAppMiddleware _(alias: 'sas.app.auth')_: A middleware to verify incoming webhook requests
50+
- SwAppIframeMiddleware _(alias: 'sas.app.auth.iframe')_: A middleware to verify incoming requests from Iframe Shopware
5051

5152
## Change log
5253
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "sas/shopware-laravel-sdk",
33
"description": "Shopware SDK for Laravel 8",
44
"type": "library",
5-
"version": "1.2.1",
5+
"version": "1.3.0",
66
"require": {
77
"php": "^7.4 || ^8.0",
88
"ext-json": "*",

composer.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Sas\ShopwareLaravelSdk\Http\Middleware;
4+
5+
use Illuminate\Http\Request;
6+
use Illuminate\Support\Facades\Auth;
7+
use Sas\ShopwareLaravelSdk\Models\SwShop;
8+
use Vin\ShopwareSdk\Data\Webhook\Shop;
9+
use Vin\ShopwareSdk\Data\Webhook\ShopRequest;
10+
use Vin\ShopwareSdk\Exception\AuthorizationFailedException;
11+
use Vin\ShopwareSdk\Service\WebhookAuthenticator;
12+
13+
class SwAppIframeMiddleware extends SwAppMiddleware
14+
{
15+
protected function authenticatePostRequest(Request $request): SwShop
16+
{
17+
$requestContent = json_decode($request->getContent(), true);
18+
$sourceRequest = $requestContent['source'];
19+
$shopId = $sourceRequest[ShopRequest::SHOP_ID_REQUEST_PARAMETER];
20+
21+
$shop = $this->shopRepository->getShopById($shopId);
22+
23+
$authenticated = $shop && $this->checkPostRequest($sourceRequest, $shop->shop_secret);
24+
25+
if (!$authenticated) {
26+
throw new AuthorizationFailedException($request->getMethod() . ' is not supported or the data is invalid');
27+
}
28+
29+
return $shop;
30+
}
31+
32+
private function checkPostRequest(array $sourceRequests, string $shopSecret): bool
33+
{
34+
$shopwareShopSignature = $sourceRequests['shopware-shop-signature'];
35+
36+
unset($sourceRequests[ShopRequest::SHOP_SIGNATURE_REQUEST_PARAMETER]);
37+
38+
$results = [];
39+
foreach ($sourceRequests as $key => $sourceRequest) {
40+
if (!in_array($key, self::REQUIRED_KEYS)) {
41+
$sourceRequest = urlencode($sourceRequest);
42+
}
43+
44+
$results[$key] = $sourceRequest;
45+
}
46+
47+
$queryString = htmlspecialchars_decode(urldecode(http_build_query($results)));
48+
$hmac = \hash_hmac('sha256', $queryString, $shopSecret);
49+
50+
return \hash_equals($hmac, $shopwareShopSignature);
51+
}
52+
}

src/Http/Middleware/SwAppMiddleware.php

+72-40
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,24 @@
55
use Closure;
66
use Illuminate\Http\Request;
77
use Illuminate\Support\Facades\Auth;
8-
use Vin\ShopwareSdk\Data\Webhook\ShopRequest;
8+
use Sas\ShopwareLaravelSdk\Models\SwShop;
99
use Sas\ShopwareLaravelSdk\Repositories\ShopRepository;
10+
use Vin\ShopwareSdk\Data\Webhook\Shop;
11+
use Vin\ShopwareSdk\Data\Webhook\ShopRequest;
1012
use Vin\ShopwareSdk\Exception\AuthorizationFailedException;
1113
use Vin\ShopwareSdk\Service\WebhookAuthenticator;
1214

1315
class SwAppMiddleware
1416
{
15-
private ShopRepository $shopRepository;
17+
public const REQUIRED_KEYS = [
18+
ShopRequest::SHOP_ID_REQUEST_PARAMETER,
19+
ShopRequest::SHOP_URL_REQUEST_PARAMETER,
20+
ShopRequest::SHOPWARE_VERSION_REQUEST_PARAMETER,
21+
ShopRequest::SHOP_SIGNATURE_REQUEST_PARAMETER,
22+
ShopRequest::TIME_STAMP_REQUEST_PARAMETER,
23+
];
24+
25+
protected ShopRepository $shopRepository;
1626

1727
public function __construct(ShopRepository $shopRepository)
1828
{
@@ -22,37 +32,21 @@ public function __construct(ShopRepository $shopRepository)
2232
/**
2333
* Handle an incoming request.
2434
*
25-
* @param \Illuminate\Http\Request $request
26-
* @param \Closure $next
27-
* @param string|null ...$guards
35+
* @param Request $request
36+
* @param Closure $next
37+
* @param string|null ...$guards
2838
* @return mixed
2939
*/
3040
public function handle(Request $request, Closure $next, ...$guards)
3141
{
32-
$authenticated = false;
3342
$shop = null;
3443

3544
if ($request->getMethod() === 'POST' && $this->supportsPostRequest($request)) {
36-
$requestContent = json_decode($request->getContent(), true);
37-
$shopId = $requestContent['source'][ShopRequest::SHOP_ID_REQUEST_PARAMETER];
38-
39-
$shop = $this->shopRepository->getShopById($shopId);
40-
41-
$authenticated = $shop && WebhookAuthenticator::authenticatePostRequest($shop->shop_secret);
45+
$shop = $this->authenticatePostRequest($request);
4246
} elseif ($request->getMethod() === 'GET' && $this->supportsGetRequest($request)) {
43-
$shopId = $request->query->get(ShopRequest::SHOP_ID_REQUEST_PARAMETER);
44-
$shop = $this->shopRepository->getShopById($shopId);
45-
46-
$authenticated = $shop && WebhookAuthenticator::authenticateGetRequest($shop->shop_secret);
47-
}elseif ($request->getMethod() === 'DELETE' && $this->supportsGetRequest($request)) {
48-
$shopId = $request->query->get(ShopRequest::SHOP_ID_REQUEST_PARAMETER);
49-
$shop = $this->shopRepository->getShopById($shopId);
50-
51-
$authenticated = $shop && WebhookAuthenticator::authenticateGetRequest($shop->shop_secret);
52-
}
53-
54-
if (!$authenticated) {
55-
throw new AuthorizationFailedException($request->getMethod() . ' is not supported or the data is invalid');
47+
$shop = $this->authenticateGetRequest($request);
48+
} elseif ($request->getMethod() === 'DELETE' && $this->supportsGetRequest($request)) {
49+
$shop = $this->authenticateDeleteRequest($request);
5650
}
5751

5852
// TODO: set custom guard for app
@@ -63,7 +57,18 @@ public function handle(Request $request, Closure $next, ...$guards)
6357
return $next($request);
6458
}
6559

66-
private function supportsPostRequest(Request $request): bool
60+
protected function checkRequiredKeys(array $data): bool
61+
{
62+
foreach (self::REQUIRED_KEYS as $key) {
63+
if (!array_key_exists($key, $data)) {
64+
return false;
65+
}
66+
}
67+
68+
return true;
69+
}
70+
71+
protected function supportsPostRequest(Request $request): bool
6772
{
6873
$requestContent = json_decode($request->getContent(), true);
6974

@@ -76,26 +81,53 @@ private function supportsPostRequest(Request $request): bool
7681
return $this->checkRequiredKeys($requestContent['source']);
7782
}
7883

79-
private function supportsGetRequest(Request $request): bool
84+
protected function supportsGetRequest(Request $request): bool
8085
{
8186
return $this->checkRequiredKeys($request->query->all());
8287
}
8388

84-
private function checkRequiredKeys(array $data): bool {
85-
$requiredKeys = [
86-
ShopRequest::SHOP_ID_REQUEST_PARAMETER,
87-
ShopRequest::SHOP_URL_REQUEST_PARAMETER,
88-
ShopRequest::SHOPWARE_VERSION_REQUEST_PARAMETER,
89-
ShopRequest::SHOP_SIGNATURE_REQUEST_PARAMETER,
90-
ShopRequest::TIME_STAMP_REQUEST_PARAMETER,
91-
];
89+
protected function authenticatePostRequest(Request $request): SwShop
90+
{
91+
$requestContent = json_decode($request->getContent(), true);
92+
$sourceRequest = $requestContent['source'];
93+
$shopId = $sourceRequest[ShopRequest::SHOP_ID_REQUEST_PARAMETER];
9294

93-
foreach ($requiredKeys as $key) {
94-
if (!array_key_exists($key, $data)) {
95-
return false;
96-
}
95+
$shop = $this->shopRepository->getShopById($shopId);
96+
97+
$authenticated = $shop && WebhookAuthenticator::authenticatePostRequest($shop->shop_secret);
98+
99+
if (!$authenticated) {
100+
throw new AuthorizationFailedException($request->getMethod() . ' is not supported or the data is invalid');
97101
}
98102

99-
return true;
103+
return $shop;
104+
}
105+
106+
protected function authenticateGetRequest(Request $request): SwShop
107+
{
108+
$shopId = $request->query->get(ShopRequest::SHOP_ID_REQUEST_PARAMETER);
109+
$shop = $this->shopRepository->getShopById($shopId);
110+
111+
$authenticated = $shop && WebhookAuthenticator::authenticateGetRequest($shop->shop_secret);
112+
113+
if (!$authenticated) {
114+
throw new AuthorizationFailedException($request->getMethod() . ' is not supported or the data is invalid');
115+
}
116+
117+
return $shop;
118+
}
119+
120+
protected function authenticateDeleteRequest(Request $request): SwShop
121+
{
122+
$shopId = $request->query->get(ShopRequest::SHOP_ID_REQUEST_PARAMETER);
123+
$shop = $this->shopRepository->getShopById($shopId);
124+
125+
$authenticated = $shop && WebhookAuthenticator::authenticateGetRequest($shop->shop_secret);
126+
127+
if (!$authenticated) {
128+
throw new AuthorizationFailedException($request->getMethod() . ' is not supported or the data is invalid');
129+
}
130+
131+
return $shop;
100132
}
101133
}

src/Models/SwShop.php

+7
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66
use Illuminate\Database\Eloquent\Model;
77
use Illuminate\Auth\Authenticatable;
88

9+
/**
10+
* @property string $shop_id
11+
* @property string $shop_url
12+
* @property string $shop_secret
13+
* @property string $api_key
14+
* @property string $secret_key
15+
*/
916
class SwShop extends Model implements AuthenticatableContract
1017
{
1118
use Authenticatable;

src/ServiceProvider/ShopwareSdkServiceProvider.php

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Sas\ShopwareLaravelSdk\ServiceProvider;
44

5+
use Sas\ShopwareLaravelSdk\Http\Middleware\SwAppIframeMiddleware;
56
use Sas\ShopwareLaravelSdk\Http\Middleware\SwAppMiddleware;
67
use Illuminate\Support\ServiceProvider;
78
use Sas\ShopwareLaravelSdk\Utils\AppHelper;
@@ -40,5 +41,6 @@ public function boot(): void
4041
$this->loadRoutesFrom(__DIR__ . '/../routes/app.php');
4142

4243
$this->app->get('router')->aliasMiddleware('sas.app.auth', SwAppMiddleware::class);
44+
$this->app->get('router')->aliasMiddleware('sas.app.auth.iframe', SwAppIframeMiddleware::class);
4345
}
4446
}

0 commit comments

Comments
 (0)