Skip to content

Commit 3eceaba

Browse files
committed
Merge pull request #4 from aws/cleanup
Code cleanup.
2 parents e1cb539 + fbceec2 commit 3eceaba

File tree

7 files changed

+126
-187
lines changed

7 files changed

+126
-187
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
namespace Aws\Sns\Exception;
3+
4+
/**
5+
* Runtime exception thrown by the SNS Message Validator.
6+
*/
7+
class InvalidSnsMessageException extends \RuntimeException
8+
{
9+
}

src/Message.php

Lines changed: 46 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
<?php
2-
32
namespace Aws\Sns;
43

5-
class Message
4+
/**
5+
* Represents an SNS message received over http(s).
6+
*/
7+
class Message implements \ArrayAccess, \IteratorAggregate
68
{
79
private static $requiredKeys = [
810
'__default' => [
@@ -57,34 +59,35 @@ public static function fromRawPostData()
5759
throw new \RuntimeException('Invalid POST data.');
5860
}
5961

60-
return self::fromArray($data);
62+
return new Message($data);
6163
}
6264

6365
/**
64-
* Creates a Message object from an array of raw message data
66+
* Creates a Message object from an array of raw message data.
6567
*
6668
* @param array $data The message data.
6769
*
68-
* @return Message
6970
* @throws \InvalidArgumentException If a valid type is not provided or
7071
* there are other required keys missing.
7172
*/
72-
public static function fromArray(array $data)
73+
public function __construct(array $data)
7374
{
7475
// Make sure the type key is set
7576
if (!isset($data['Type'])) {
76-
throw new \InvalidArgumentException('The "Type" key must be '
77-
. 'provided to instantiate a Message object.');
77+
throw new \InvalidArgumentException(
78+
'The "Type" must be provided to instantiate a Message object.'
79+
);
7880
}
7981

80-
// Determine required keys and create a collection from the message data
82+
// Determine the required keys for this message type.
8183
$requiredKeys = array_merge(
8284
self::$requiredKeys['__default'],
8385
isset(self::$requiredKeys[$data['Type']]) ?
8486
self::$requiredKeys[$data['Type']]
8587
: []
8688
);
8789

90+
// Ensure that all the required keys are provided.
8891
foreach ($requiredKeys as $key) {
8992
if (!isset($data[$key])) {
9093
throw new \InvalidArgumentException(
@@ -93,37 +96,12 @@ public static function fromArray(array $data)
9396
}
9497
}
9598

96-
return new self($data);
97-
}
98-
99-
/**
100-
* @param array $data Message data with all required keys.
101-
*/
102-
public function __construct(array $data)
103-
{
10499
$this->data = $data;
105100
}
106101

107-
/**
108-
* Get the entire message data as an array.
109-
*
110-
* @return array
111-
*/
112-
public function getData()
113-
{
114-
return $this->data;
115-
}
116-
117-
/**
118-
* Gets a single key from the message data.
119-
*
120-
* @param string $key Key to retrieve
121-
*
122-
* @return string
123-
*/
124-
public function get($key)
102+
public function getIterator()
125103
{
126-
return isset($this->data[$key]) ? $this->data[$key] : null;
104+
return new \ArrayIterator($this->data);
127105
}
128106

129107
/**
@@ -136,11 +114,41 @@ public function getStringToSign()
136114
{
137115
$stringToSign = '';
138116
foreach (self::$signableKeys as $key) {
139-
if ($value = $this->get($key)) {
140-
$stringToSign .= "{$key}\n{$value}\n";
117+
if (isset($this[$key])) {
118+
$stringToSign .= "{$key}\n{$this[$key]}\n";
141119
}
142120
}
143121

144122
return $stringToSign;
145123
}
124+
125+
public function offsetExists($key)
126+
{
127+
return isset($this->data[$key]);
128+
}
129+
130+
public function offsetGet($key)
131+
{
132+
return isset($this->data[$key]) ? $this->data[$key] : null;
133+
}
134+
135+
public function offsetSet($key, $value)
136+
{
137+
$this->data[$key] = $value;
138+
}
139+
140+
public function offsetUnset($key)
141+
{
142+
unset($this->data[$key]);
143+
}
144+
145+
/**
146+
* Get all the message data as a plain array.
147+
*
148+
* @return array
149+
*/
150+
public function toArray()
151+
{
152+
return $this->data;
153+
}
146154
}

src/MessageValidator.php

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
<?php
2-
32
namespace Aws\Sns;
43

4+
use Aws\Sns\Exception\InvalidSnsMessageException;
5+
56
/**
6-
* This class uses openssl to verify SNS messages to ensure that they were sent
7-
* by AWS.
7+
* Uses openssl to verify SNS messages to ensure that they were sent by AWS.
88
*/
99
class MessageValidator
1010
{
@@ -26,47 +26,39 @@ class MessageValidator
2626
public function __construct(callable $remoteFileReader = null)
2727
{
2828
$this->remoteFileReader = $remoteFileReader ?: 'file_get_contents';
29-
30-
if (!extension_loaded('openssl')) {
31-
//@codeCoverageIgnoreStart
32-
throw new \RuntimeException('The openssl extension is required to '
33-
. 'use the SNS message validator. Please install this '
34-
. 'extension in order to use this feature.');
35-
//@codeCoverageIgnoreEnd
36-
}
3729
}
3830

3931
/**
4032
* Validates a message from SNS to ensure that it was delivered by AWS
4133
*
4234
* @param Message $message The message to validate
4335
*
44-
* @throws MessageValidatorException If the certificate cannot be
36+
* @throws InvalidSnsMessageException If the certificate cannot be
4537
* retrieved, if the certificate's source cannot be verified, or if the
4638
* message's signature is invalid.
4739
*/
4840
public function validate(Message $message)
4941
{
50-
$this->validateSignatureVersion($message->get('SignatureVersion'));
42+
$this->validateSignatureVersion($message['SignatureVersion']);
5143

52-
$certUrl = $message->get('SigningCertURL');
44+
$certUrl = $message['SigningCertURL'];
5345
$this->validateUrl($certUrl);
5446

5547
// Get the cert itself and extract the public key
5648
$certificate = call_user_func($this->remoteFileReader, $certUrl);
5749
$key = openssl_get_publickey($certificate);
5850
if (!$key) {
59-
throw new MessageValidatorException(
51+
throw new InvalidSnsMessageException(
6052
'Cannot get the public key from the certificate.'
6153
);
6254
}
6355

6456
// Verify the signature of the message
6557
$content = $message->getStringToSign();
66-
$signature = base64_decode($message->get('Signature'));
58+
$signature = base64_decode($message['Signature']);
6759

6860
if (!openssl_verify($content, $signature, $key, OPENSSL_ALGO_SHA1)) {
69-
throw new MessageValidatorException(
61+
throw new InvalidSnsMessageException(
7062
'The message signature is invalid.'
7163
);
7264
}
@@ -85,7 +77,7 @@ public function isValid(Message $message)
8577
try {
8678
$this->validate($message);
8779
return true;
88-
} catch (MessageValidatorException $e) {
80+
} catch (InvalidSnsMessageException $e) {
8981
return false;
9082
}
9183
}
@@ -96,7 +88,7 @@ public function isValid(Message $message)
9688
*
9789
* @param string $url
9890
*
99-
* @throws MessageValidatorException if the cert url is invalid
91+
* @throws InvalidSnsMessageException if the cert url is invalid
10092
*/
10193
private function validateUrl($url)
10294
{
@@ -109,7 +101,7 @@ private function validateUrl($url)
109101
|| substr($url, -4) !== '.pem'
110102
|| !preg_match($hostPattern, $parsed['host'])
111103
) {
112-
throw new MessageValidatorException(
104+
throw new InvalidSnsMessageException(
113105
'The certificate is located on an invalid domain.'
114106
);
115107
}
@@ -118,7 +110,7 @@ private function validateUrl($url)
118110
private function validateSignatureVersion($version)
119111
{
120112
if ($version !== self::SUPPORTED_SIGNATURE_VERSION) {
121-
throw new MessageValidatorException(
113+
throw new InvalidSnsMessageException(
122114
"Only v1 signatures can be validated; v{$version} provided"
123115
);
124116
}

src/MessageValidatorException.php

Lines changed: 0 additions & 10 deletions
This file was deleted.

tests/MessageTest.php

Lines changed: 19 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<?php
2-
32
namespace Aws\Sns;
43

54
/**
@@ -23,19 +22,17 @@ class MessageTest extends \PHPUnit_Framework_TestCase
2322
public function testGetters()
2423
{
2524
$message = new Message($this->messageData);
26-
$this->assertInternalType('array', $message->getData());
25+
$this->assertInternalType('array', $message->toArray());
2726

2827
foreach ($this->messageData as $key => $expectedValue) {
29-
$this->assertEquals($expectedValue, $message->get($key));
28+
$this->assertTrue(isset($message[$key]));
29+
$this->assertEquals($expectedValue, $message[$key]);
3030
}
3131
}
3232

3333
public function testFactorySucceedsWithGoodData()
3434
{
35-
$this->assertInstanceOf(
36-
'Aws\Sns\Message',
37-
Message::fromArray($this->messageData)
38-
);
35+
$this->assertInstanceOf('Aws\Sns\Message', new Message($this->messageData));
3936
}
4037

4138
/**
@@ -45,15 +42,15 @@ public function testFactoryFailsWithNoType()
4542
{
4643
$data = $this->messageData;
4744
unset($data['Type']);
48-
Message::fromArray($data);
45+
new Message($data);
4946
}
5047

5148
/**
5249
* @expectedException \InvalidArgumentException
5350
*/
5451
public function testFactoryFailsWithMissingData()
5552
{
56-
Message::fromArray(array('Type' => 'Notification'));
53+
new Message(['Type' => 'Notification']);
5754
}
5855

5956
public function testCanCreateFromRawPost()
@@ -90,35 +87,18 @@ public function testCreateFromRawPostFailsWithMissingData()
9087
unset($_SERVER['HTTP_X_AMZ_SNS_MESSAGE_TYPE']);
9188
}
9289

93-
/**
94-
* @dataProvider getDataForStringToSignTest
95-
*/
96-
public function testBuildsStringToSignCorrectly(
97-
array $messageData,
98-
$expectedSubject,
99-
$expectedStringToSign
100-
) {
101-
$message = new Message($messageData);
102-
$this->assertEquals($expectedSubject, $message->get('Subject'));
103-
$this->assertEquals($expectedStringToSign, $message->getStringToSign());
104-
}
105-
106-
public function getDataForStringToSignTest()
107-
{
108-
$testCases = array();
109-
110-
// Test case where one key is not signable
111-
$testCases[0] = array();
112-
$testCases[0][] = array(
113-
'TopicArn' => 'd',
114-
'Message' => 'a',
115-
'Timestamp' => 'c',
116-
'Type' => 'e',
117-
'MessageId' => 'b',
118-
'FooBar' => 'f',
119-
);
120-
$testCases[0][] = null;
121-
$testCases[0][] = <<< STRINGTOSIGN
90+
public function testBuildsStringToSignCorrectly( ) {
91+
$message = new Message([
92+
'TopicArn' => 'd',
93+
'Message' => 'a',
94+
'Timestamp' => 'c',
95+
'Type' => 'e',
96+
'MessageId' => 'b',
97+
'FooBar' => 'f',
98+
'Signature' => true,
99+
'SigningCertURL' => true,
100+
]);
101+
$stringToSign = <<< STRINGTOSIGN
122102
Message
123103
a
124104
MessageId
@@ -131,34 +111,6 @@ public function getDataForStringToSignTest()
131111
e
132112
133113
STRINGTOSIGN;
134-
135-
// Test case where all keys are signable
136-
$testCases[1] = array();
137-
$testCases[1][] = array(
138-
'TopicArn' => 'e',
139-
'Message' => 'a',
140-
'Timestamp' => 'd',
141-
'Type' => 'f',
142-
'MessageId' => 'b',
143-
'Subject' => 'c',
144-
);
145-
$testCases[1][] = 'c';
146-
$testCases[1][] = <<< STRINGTOSIGN
147-
Message
148-
a
149-
MessageId
150-
b
151-
Subject
152-
c
153-
Timestamp
154-
d
155-
TopicArn
156-
e
157-
Type
158-
f
159-
160-
STRINGTOSIGN;
161-
162-
return $testCases;
114+
$this->assertEquals($stringToSign, $message->getStringToSign());
163115
}
164116
}

0 commit comments

Comments
 (0)