Skip to content

Commit bf6884f

Browse files
authored
Merge pull request #51 from shopware/feat/support-app-secret-rotation
feat: support app secret rotation
2 parents be54610 + d5bed93 commit bf6884f

23 files changed

+2499
-185
lines changed

src/Adapter/DynamoDB/DynamoDBRepository.php

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,24 @@ public function createShopStruct(string $shopId, string $shopUrl, string $shopSe
2828

2929
public function createShop(ShopInterface $shop): void
3030
{
31+
$item = [
32+
'id' => ['S' => $shop->getShopId()],
33+
'active' => ['BOOL' => $shop->isShopActive() ? '1' : '0'],
34+
'confirmed' => ['BOOL' => $shop->isRegistrationConfirmed() ? '1' : '0'],
35+
'url' => ['S' => $shop->getShopUrl()],
36+
'secret' => ['S' => $shop->getShopSecret()],
37+
'clientId' => ['S' => (string) $shop->getShopClientId()],
38+
'clientSecret' => ['S' => (string) $shop->getShopClientSecret()],
39+
'pendingShopSecret' => ['S' => (string) $shop->getPendingShopSecret()],
40+
'pendingShopUrl' => ['S' => (string) $shop->getPendingShopUrl()],
41+
'previousShopSecret' => ['S' => (string) $shop->getPreviousShopSecret()],
42+
'secretsRotatedAt' => ['S' => (string) ($shop->getSecretsRotatedAt()?->getTimestamp() ?? '')],
43+
'hasVerifiedWithDoubleSignature' => ['BOOL' => $shop->hasVerifiedWithDoubleSignature() ? '1' : '0'],
44+
];
45+
3146
$this->client->putItem(new PutItemInput([
3247
'TableName' => $this->tableName,
33-
'Item' => [
34-
'id' => ['S' => $shop->getShopId()],
35-
'active' => ['BOOL' => $shop->isShopActive() ? '1' : '0'],
36-
'url' => ['S' => $shop->getShopUrl()],
37-
'secret' => ['S' => $shop->getShopSecret()],
38-
'clientId' => ['S' => (string) $shop->getShopClientId()],
39-
'clientSecret' => ['S' => (string) $shop->getShopClientSecret()],
40-
],
48+
'Item' => $item,
4149
]));
4250
}
4351

@@ -71,13 +79,61 @@ public function getShopFromId(string $shopId): ShopInterface|null
7179
$active = false;
7280
}
7381

82+
$confirmed = true;
83+
if (isset($item['confirmed'])) {
84+
$confirmed = $item['confirmed']->getBool();
85+
if ($confirmed === null) {
86+
$confirmed = false;
87+
}
88+
}
89+
90+
$pendingShopSecret = isset($item['pendingShopSecret']) ? $item['pendingShopSecret']->getS() : null;
91+
$pendingShopUrl = isset($item['pendingShopUrl']) ? $item['pendingShopUrl']->getS() : null;
92+
$previousShopSecret = isset($item['previousShopSecret']) ? $item['previousShopSecret']->getS() : null;
93+
94+
if ($pendingShopSecret === '') {
95+
$pendingShopSecret = null;
96+
}
97+
98+
if ($pendingShopUrl === '') {
99+
$pendingShopUrl = null;
100+
}
101+
102+
if ($previousShopSecret === '') {
103+
$previousShopSecret = null;
104+
}
105+
106+
$secretsRotatedAt = null;
107+
108+
if (isset($item['secretsRotatedAt'])) {
109+
$timestamp = $item['secretsRotatedAt']->getS();
110+
if ($timestamp !== null && $timestamp !== '') {
111+
$secretsRotatedAt = (new \DateTimeImmutable())->setTimestamp((int) $timestamp);
112+
}
113+
}
114+
115+
$hasVerifiedWithDoubleSignature = false;
116+
if (isset($item['hasVerifiedWithDoubleSignature'])) {
117+
$hasVerifiedWithDoubleSignature = $item['hasVerifiedWithDoubleSignature']->getBool();
118+
119+
if ($hasVerifiedWithDoubleSignature === null) {
120+
$hasVerifiedWithDoubleSignature = false;
121+
}
122+
}
123+
74124
return new DynamoDBShop(
75125
$item['id']->getS() ?? '',
76126
$item['url']->getS() ?? '',
77127
$item['secret']->getS() ?? '',
78128
$shopClientId,
79129
$shopClientSecret,
80130
$active,
131+
$pendingShopSecret,
132+
$pendingShopUrl,
133+
$previousShopSecret,
134+
$secretsRotatedAt,
135+
$hasVerifiedWithDoubleSignature,
136+
$confirmed,
81137
);
82138
}
83139

@@ -88,16 +144,22 @@ public function updateShop(ShopInterface $shop): void
88144
'Key' => [
89145
'id' => ['S' => $shop->getShopId()],
90146
],
91-
'UpdateExpression' => 'SET active = :active, #u = :url, secret = :secret, clientId = :clientId, clientSecret = :clientSecret',
147+
'UpdateExpression' => 'SET active = :active, confirmed = :confirmed, #u = :url, secret = :secret, clientId = :clientId, clientSecret = :clientSecret, pendingShopSecret = :pendingShopSecret, pendingShopUrl = :pendingShopUrl, previousShopSecret = :previousShopSecret, secretsRotatedAt = :secretsRotatedAt, hasVerifiedWithDoubleSignature = :hasVerifiedWithDoubleSignature',
92148
'ExpressionAttributeNames' => [
93149
'#u' => 'url',
94150
],
95151
'ExpressionAttributeValues' => [
96152
':active' => ['BOOL' => $shop->isShopActive() ? '1' : '0'],
153+
':confirmed' => ['BOOL' => $shop->isRegistrationConfirmed() ? '1' : '0'],
97154
':url' => ['S' => $shop->getShopUrl()],
98155
':secret' => ['S' => $shop->getShopSecret()],
99156
':clientId' => ['S' => (string) $shop->getShopClientId()],
100157
':clientSecret' => ['S' => (string) $shop->getShopClientSecret()],
158+
':pendingShopSecret' => ['S' => (string) $shop->getPendingShopSecret()],
159+
':pendingShopUrl' => ['S' => (string) $shop->getPendingShopUrl()],
160+
':previousShopSecret' => ['S' => (string) $shop->getPreviousShopSecret()],
161+
':secretsRotatedAt' => ['S' => (string) ($shop->getSecretsRotatedAt()?->getTimestamp() ?? '')],
162+
':hasVerifiedWithDoubleSignature' => ['BOOL' => $shop->hasVerifiedWithDoubleSignature() ? '1' : '0'],
101163
],
102164
]));
103165
}

src/Adapter/DynamoDB/DynamoDBShop.php

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,23 @@
88

99
class DynamoDBShop implements ShopInterface
1010
{
11-
public function __construct(public string $shopId, public string $shopUrl, public string $shopSecret, public ?string $shopClientId = null, public ?string $shopClientSecret = null, public bool $active = false)
12-
{
11+
public function __construct(
12+
public string $shopId,
13+
public string $shopUrl,
14+
public string $shopSecret,
15+
public ?string $shopClientId = null,
16+
public ?string $shopClientSecret = null,
17+
public bool $active = false,
18+
public ?string $pendingShopSecret = null,
19+
public ?string $pendingShopUrl = null,
20+
public ?string $previousShopSecret = null,
21+
public ?\DateTimeImmutable $secretsRotatedAt = null,
22+
/**
23+
* @deprecated tag:v6.0.0 - Will be removed. Double signature verification will always be enforced.
24+
*/
25+
public bool $hasVerifiedWithDoubleSignature = false,
26+
public bool $registrationConfirmed = false,
27+
) {
1328
}
1429

1530
public function isShopActive(): bool
@@ -32,6 +47,16 @@ public function getShopSecret(): string
3247
return $this->shopSecret;
3348
}
3449

50+
public function getPreviousShopSecret(): ?string
51+
{
52+
return $this->previousShopSecret;
53+
}
54+
55+
public function getPendingShopSecret(): ?string
56+
{
57+
return $this->pendingShopSecret;
58+
}
59+
3560
public function getShopClientId(): ?string
3661
{
3762
return $this->shopClientId;
@@ -42,6 +67,16 @@ public function getShopClientSecret(): ?string
4267
return $this->shopClientSecret;
4368
}
4469

70+
public function getPendingShopUrl(): ?string
71+
{
72+
return $this->pendingShopUrl;
73+
}
74+
75+
public function getSecretsRotatedAt(): ?\DateTimeImmutable
76+
{
77+
return $this->secretsRotatedAt;
78+
}
79+
4580
public function setShopApiCredentials(string $clientId, string $clientSecret): ShopInterface
4681
{
4782
$this->shopClientId = $clientId;
@@ -50,6 +85,13 @@ public function setShopApiCredentials(string $clientId, string $clientSecret): S
5085
return $this;
5186
}
5287

88+
public function setShopSecret(string $secret): ShopInterface
89+
{
90+
$this->shopSecret = $secret;
91+
92+
return $this;
93+
}
94+
5395
public function setShopUrl(string $url): ShopInterface
5496
{
5597
$this->shopUrl = $url;
@@ -63,4 +105,62 @@ public function setShopActive(bool $active): ShopInterface
63105

64106
return $this;
65107
}
108+
109+
public function setPendingShopSecret(?string $secret): ShopInterface
110+
{
111+
$this->pendingShopSecret = $secret;
112+
113+
return $this;
114+
}
115+
116+
public function setPendingShopUrl(?string $shopUrl): ShopInterface
117+
{
118+
$this->pendingShopUrl = $shopUrl;
119+
120+
return $this;
121+
}
122+
123+
public function setPreviousShopSecret(string $secret): ShopInterface
124+
{
125+
$this->previousShopSecret = $secret;
126+
127+
return $this;
128+
}
129+
130+
public function setSecretsRotatedAt(\DateTimeImmutable $updatedAt): ShopInterface
131+
{
132+
$this->secretsRotatedAt = $updatedAt;
133+
134+
return $this;
135+
}
136+
137+
public function isRegistrationConfirmed(): bool
138+
{
139+
return $this->registrationConfirmed;
140+
}
141+
142+
public function setRegistrationConfirmed(): ShopInterface
143+
{
144+
$this->registrationConfirmed = true;
145+
146+
return $this;
147+
}
148+
149+
/**
150+
* @deprecated tag:v6.0.0 - Will be removed. Double signature verification will always be enforced.
151+
*/
152+
public function setVerifiedWithDoubleSignature(): ShopInterface
153+
{
154+
$this->hasVerifiedWithDoubleSignature = true;
155+
156+
return $this;
157+
}
158+
159+
/**
160+
* @deprecated tag:v6.0.0 - Will be removed. Double signature verification will always be enforced.
161+
*/
162+
public function hasVerifiedWithDoubleSignature(): bool
163+
{
164+
return $this->hasVerifiedWithDoubleSignature;
165+
}
66166
}

src/AppConfiguration.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ class AppConfiguration
99
public function __construct(
1010
private readonly string $appName,
1111
private readonly string $appSecret,
12-
private readonly string $registrationConfirmationUrl
12+
private readonly string $registrationConfirmationUrl,
13+
private readonly bool $enforceDoubleSignature = false
1314
) {
1415
}
1516

@@ -27,4 +28,12 @@ public function getRegistrationConfirmUrl(): string
2728
{
2829
return $this->registrationConfirmationUrl;
2930
}
31+
32+
/**
33+
* @deprecated tag:v6.0.0 - Will be removed. Double signature verification will always be enforced.
34+
*/
35+
public function enforceDoubleSignature(): bool
36+
{
37+
return $this->enforceDoubleSignature;
38+
}
3039
}

0 commit comments

Comments
 (0)