Skip to content

Commit 4f6c042

Browse files
authored
Merge pull request #27 from aws/lambda_url_casing_fixes
Lambda SNS Casing Fix
2 parents 46cc7e0 + a51483f commit 4f6c042

File tree

3 files changed

+200
-3
lines changed

3 files changed

+200
-3
lines changed

src/Message.php

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,15 @@ class Message implements \ArrayAccess, \IteratorAggregate
1515
'TopicArn',
1616
'Type',
1717
'Signature',
18-
'SigningCertURL',
18+
['SigningCertURL', 'SigningCertUrl'],
1919
'SignatureVersion',
2020
];
2121

22+
private static $subscribeKeys = [
23+
['SubscribeURL', 'SubscribeUrl'],
24+
'Token'
25+
];
26+
2227
/** @var array The message data */
2328
private $data;
2429

@@ -81,7 +86,7 @@ public function __construct(array $data)
8186
if ($data['Type'] === 'SubscriptionConfirmation'
8287
|| $data['Type'] === 'UnsubscribeConfirmation'
8388
) {
84-
$this->validateRequiredKeys($data, ['SubscribeURL', 'Token']);
89+
$this->validateRequiredKeys($data, self::$subscribeKeys);
8590
}
8691

8792
$this->data = $data;
@@ -125,7 +130,23 @@ public function toArray()
125130
private function validateRequiredKeys(array $data, array $keys)
126131
{
127132
foreach ($keys as $key) {
128-
if (!isset($data[$key])) {
133+
$keyIsArray = is_array($key);
134+
if (!$keyIsArray) {
135+
$found = isset($data[$key]);
136+
} else {
137+
$found = false;
138+
foreach ($key as $keyOption) {
139+
if (isset($data[$keyOption])) {
140+
$found = true;
141+
break;
142+
}
143+
}
144+
}
145+
146+
if (!$found) {
147+
if ($keyIsArray) {
148+
$key = $key[0];
149+
}
129150
throw new \InvalidArgumentException(
130151
"\"{$key}\" is required to verify the SNS Message."
131152
);

src/MessageValidator.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,30 @@ class MessageValidator
2727
private static $defaultHostPattern
2828
= '/^sns\.[a-zA-Z0-9\-]{3,}\.amazonaws\.com(\.cn)?$/';
2929

30+
private static function isLambdaStyle(Message $message)
31+
{
32+
return isset($message['SigningCertUrl']);
33+
}
34+
35+
private static function convertLambdaMessage(Message $lambdaMessage)
36+
{
37+
$keyReplacements = [
38+
'SigningCertUrl' => 'SigningCertURL',
39+
'SubscribeUrl' => 'SubscribeURL',
40+
'UnsubscribeUrl' => 'UnsubscribeURL',
41+
];
42+
43+
$message = clone $lambdaMessage;
44+
foreach ($keyReplacements as $lambdaKey => $canonicalKey) {
45+
if (isset($message[$lambdaKey])) {
46+
$message[$canonicalKey] = $message[$lambdaKey];
47+
unset($message[$lambdaKey]);
48+
}
49+
}
50+
51+
return $message;
52+
}
53+
3054
/**
3155
* Constructs the Message Validator object and ensures that openssl is
3256
* installed.
@@ -55,6 +79,10 @@ public function __construct(
5579
*/
5680
public function validate(Message $message)
5781
{
82+
if (self::isLambdaStyle($message)) {
83+
$message = self::convertLambdaMessage($message);
84+
}
85+
5886
// Get the certificate.
5987
$this->validateUrl($message['SigningCertURL']);
6088
$certificate = call_user_func($this->certClient, $message['SigningCertURL']);
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<?php
2+
3+
namespace Aws\Sns;
4+
5+
/**
6+
* @covers Aws\Sns\MessageValidator
7+
* @covers Aws\Sns\Message
8+
*/
9+
class FunctionalValidationsTest extends \PHPUnit_Framework_TestCase
10+
{
11+
private static $certificate =
12+
'-----BEGIN CERTIFICATE-----
13+
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
44+
6Kw4sQp+ZMgtgzM8l3fDTMEgqpLSQH+2
45+
-----END CERTIFICATE-----';
46+
47+
public function getHttpFixtures()
48+
{
49+
return [
50+
[
51+
[
52+
'Type' => "Notification",
53+
'MessageId' => "9438aee6-d476-5e20-ba25-ff24bf09d6ce",
54+
'TopicArn' => "arn:aws:sns:us-west-2:604091128280:testing1",
55+
'Subject' => "A subject",
56+
'Message' => "A message",
57+
'Timestamp' => "2017-06-20T00:15:59.380Z",
58+
'SignatureVersion' => "1",
59+
'Signature' => "WT7qMHW+jPdj/brSAX7M1jbP5OoPjn9pYmGQqrWeQgbMyVvz3D2sV72ldhCxQLqj/3TLtcTyErVqzT3AfQ8Vk55Rzxd1xnBufJ+0vIyH98b82pKOqRHOqlB72la5nY9/GF/p71BXmIChQpfv/CEZumexgLWnweJsqSMe82I6/eMmrhVZdKpBvz4Sqj+wNQW+0eYEc9bdZmEKuYIvrvTGm1MWkXmqUGuCGj5o3vFFn1GTtM895B3MyMgaSeDHI08CVfs9y1nLcrxwMvqpkHZmIwTi1jzSipYMRD8FVF6Wvq0Scy+FoYSnOWHpEsELI0SGddSqYgli9ROYiqi3DQhvHw==",
60+
'SigningCertURL' => "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-b95095beb82e8f6a046b3aafc7f4149a.pem",
61+
'UnsubscribeURL' => "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:604091128280:testing1:b061e4fd-c468-458d-9736-91c8c0c18e29",
62+
]
63+
],
64+
[
65+
[
66+
'Type' => "Notification",
67+
'MessageId' => "7317aaf2-e97a-5cf3-8123-fb3a48fabd2a",
68+
'TopicArn' => "arn:aws:sns:us-west-2:604091128280:testing1",
69+
'Message' => "A subject-less message",
70+
'Timestamp' => "2017-06-24T17:20:00.581Z",
71+
'SignatureVersion' => "1",
72+
'Signature' => "Lvtgxo8P2C3XUKT8fC7sfMRhxoK6dn/ed9B1DClmJ9GNuFF73G27lhKUsKWrLReawa+v7C1UY49qQb+lSMsBiTV0Hx7L2OKJjzll4fx+G09h2P8OK43Jk6/W05+xU0uvch6Ktp3XrBcI6KNyGFio5GAR2rCBHjdh8MsEYAWRtaVCBqJTLqnHscivOJD8u/m807wDbDhh9cQ5WnvjerUjtrDAfQJN5vHLjEPbL1owtu2FzC3rOHUL9j4TGOdZi2jhUYv8jwzNnJ05bhbtKd6HxKcTcv1JCp/4NLPa8LWYnbLRvWooDQdF2hr56EF6EKDzTtAWagoNYztwSvosQXNK+Q==",
73+
'SigningCertURL' => "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-b95095beb82e8f6a046b3aafc7f4149a.pem",
74+
'UnsubscribeURL' => "https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:604091128280:testing1:f0dd49ac-c33d-471e-812d-1f0e5116c711",
75+
]
76+
],
77+
];
78+
}
79+
80+
public function getLambdaFixtures()
81+
{
82+
return [
83+
[
84+
[
85+
'Type' => 'Notification',
86+
'MessageId' => '9438aee6-d476-5e20-ba25-ff24bf09d6ce',
87+
'TopicArn' => 'arn:aws:sns:us-west-2:604091128280:testing1',
88+
'Subject' => 'A subject',
89+
'Message' => 'A message',
90+
'Timestamp' => '2017-06-20T00:15:59.380Z',
91+
'SignatureVersion' => '1',
92+
'Signature' => 'WT7qMHW+jPdj/brSAX7M1jbP5OoPjn9pYmGQqrWeQgbMyVvz3D2sV72ldhCxQLqj/3TLtcTyErVqzT3AfQ8Vk55Rzxd1xnBufJ+0vIyH98b82pKOqRHOqlB72la5nY9/GF/p71BXmIChQpfv/CEZumexgLWnweJsqSMe82I6/eMmrhVZdKpBvz4Sqj+wNQW+0eYEc9bdZmEKuYIvrvTGm1MWkXmqUGuCGj5o3vFFn1GTtM895B3MyMgaSeDHI08CVfs9y1nLcrxwMvqpkHZmIwTi1jzSipYMRD8FVF6Wvq0Scy+FoYSnOWHpEsELI0SGddSqYgli9ROYiqi3DQhvHw==',
93+
'SigningCertUrl' => 'https://sns.us-west-2.amazonaws.com/SimpleNotificationService-b95095beb82e8f6a046b3aafc7f4149a.pem',
94+
'UnsubscribeUrl' => 'https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:604091128280:testing1:7118d01a-202e-4a65-a372-f46b0994bdae',
95+
]
96+
],
97+
[
98+
[
99+
'Type' => 'Notification',
100+
'MessageId' => '7317aaf2-e97a-5cf3-8123-fb3a48fabd2a',
101+
'TopicArn' => 'arn:aws:sns:us-west-2:604091128280:testing1',
102+
'Subject' => null,
103+
'Message' => 'A subject-less message',
104+
'Timestamp' => '2017-06-24T17:20:00.581Z',
105+
'SignatureVersion' => '1',
106+
'Signature' => 'Lvtgxo8P2C3XUKT8fC7sfMRhxoK6dn/ed9B1DClmJ9GNuFF73G27lhKUsKWrLReawa+v7C1UY49qQb+lSMsBiTV0Hx7L2OKJjzll4fx+G09h2P8OK43Jk6/W05+xU0uvch6Ktp3XrBcI6KNyGFio5GAR2rCBHjdh8MsEYAWRtaVCBqJTLqnHscivOJD8u/m807wDbDhh9cQ5WnvjerUjtrDAfQJN5vHLjEPbL1owtu2FzC3rOHUL9j4TGOdZi2jhUYv8jwzNnJ05bhbtKd6HxKcTcv1JCp/4NLPa8LWYnbLRvWooDQdF2hr56EF6EKDzTtAWagoNYztwSvosQXNK+Q==',
107+
'SigningCertUrl' => 'https://sns.us-west-2.amazonaws.com/SimpleNotificationService-b95095beb82e8f6a046b3aafc7f4149a.pem',
108+
'UnsubscribeUrl' => 'https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:604091128280:testing1:7118d01a-202e-4a65-a372-f46b0994bdae',
109+
]
110+
],
111+
];
112+
}
113+
114+
private function getMockCertServerClient()
115+
{
116+
return function () {
117+
return self::$certificate;
118+
};
119+
}
120+
121+
/**
122+
* @dataProvider getHttpFixtures
123+
*
124+
* @param array $messageData
125+
*/
126+
public function testValidatesHttpFixtures($messageData)
127+
{
128+
$validator = new MessageValidator($this->getMockCertServerClient());
129+
$message = new Message($messageData);
130+
131+
$this->assertTrue($validator->isValid($message));
132+
$this->assertNotEmpty($message['SigningCertURL']);
133+
}
134+
135+
/**
136+
* @dataProvider getLambdaFixtures
137+
*
138+
* @param array $messageData
139+
*/
140+
public function testValidatesLambdaFixtures($messageData)
141+
{
142+
$validator = new MessageValidator($this->getMockCertServerClient());
143+
$message = new Message($messageData);
144+
145+
$this->assertTrue($validator->isValid($message));
146+
$this->assertNotEmpty($message['SigningCertUrl']);
147+
}
148+
}

0 commit comments

Comments
 (0)