Skip to content

Commit

Permalink
Break the dependency on guzzle
Browse files Browse the repository at this point in the history
  • Loading branch information
jeskew committed Jun 24, 2015
1 parent 9fa01b5 commit 03ca320
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 40 deletions.
3 changes: 2 additions & 1 deletion .gitignore
@@ -1,6 +1,7 @@
phpunit.xml
Makefile
.idea
/.idea/
/*.iml
atlassian-ide-plugin.xml
.DS_Store
.swp
Expand Down
15 changes: 15 additions & 0 deletions .travis.yml
@@ -0,0 +1,15 @@
language: php

php:
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm

sudo: false

install:
- travis_retry composer update --no-interaction --prefer-dist

script: vendor/bin/phpunit
7 changes: 4 additions & 3 deletions composer.json
Expand Up @@ -13,19 +13,20 @@
],
"support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues"
"issues": "https://github.com/aws/aws-sns-message-validator/issues"
},
"require": {
"php": ">=5.4",
"ext-openssl": "*"
},
"require-dev": {
"phpunit/phpunit": "^4.0"
"phpunit/phpunit": "^4.0",
"squizlabs/php_codesniffer": "^2.3"
},
"autoload": {
"psr-4": { "Aws\\Sns\\": "src/" }
},
"autoload-dev": {
"psr-4": { "Aws\\Sns\\Test\\": "tests/" }
"psr-4": { "Aws\\Sns\\": "tests/" }
}
}
9 changes: 9 additions & 0 deletions phpunit.xml.dist
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>

<phpunit bootstrap="./vendor/autoload.php">
<testsuites>
<testsuite name="AWS SNS Message Validator Test Suite">
<directory>./tests</directory>
</testsuite>
</testsuites>
</phpunit>
38 changes: 30 additions & 8 deletions src/Message.php
@@ -1,17 +1,39 @@
<?php

namespace Aws\Sns;

class Message
{
private static $requiredKeys = [
'__default' => ['Message', 'MessageId', 'Timestamp', 'TopicArn',
'Type', 'Signature', 'SigningCertURL',],
'SubscriptionConfirmation' => ['SubscribeURL', 'Token'],
'UnsubscribeConfirmation' => ['SubscribeURL', 'Token']
'__default' => [
'Message',
'MessageId',
'Timestamp',
'TopicArn',
'Type',
'Signature',
'SigningCertURL',
],
'SubscriptionConfirmation' => [
'SubscribeURL',
'Token',
],
'UnsubscribeConfirmation' => [
'SubscribeURL',
'Token',
],
];

private static $signableKeys = ['Message', 'MessageId', 'Subject',
'SubscribeURL', 'Timestamp', 'Token', 'TopicArn', 'Type'];
private static $signableKeys = [
'Message',
'MessageId',
'Subject',
'SubscribeURL',
'Timestamp',
'Token',
'TopicArn',
'Type',
];

/** @var array The message data */
private $data;
Expand Down Expand Up @@ -58,8 +80,8 @@ public static function fromArray(array $data)
// Determine required keys and create a collection from the message data
$requiredKeys = array_merge(
self::$requiredKeys['__default'],
isset(self::$requiredKeys[$data['Type']])
? self::$requiredKeys[$data['Type']]
isset(self::$requiredKeys[$data['Type']]) ?
self::$requiredKeys[$data['Type']]
: []
);

Expand Down
28 changes: 21 additions & 7 deletions src/MessageValidator.php
@@ -1,4 +1,5 @@
<?php

namespace Aws\Sns;

/**
Expand All @@ -7,14 +8,23 @@
*/
class MessageValidator
{
/**
* @var callable
*/
private $remoteFileReader;

/**
* Constructs the Message Validator object and ensures that openssl is
* installed.
*
* @param callable $remoteFileReader
*
* @throws \RuntimeException If openssl is not installed
*/
public function __construct()
public function __construct(callable $remoteFileReader = null)
{
$this->remoteFileReader = $remoteFileReader ?: 'file_get_contents';

if (!extension_loaded('openssl')) {
//@codeCoverageIgnoreStart
throw new \RuntimeException('The openssl extension is required to '
Expand All @@ -40,20 +50,22 @@ public function validate(Message $message)
$this->validateUrl($certUrl);

// Get the cert itself and extract the public key
$certificate = file_get_contents($certUrl);
$certificate = call_user_func($this->remoteFileReader, $certUrl);
$key = openssl_get_publickey($certificate);
if (!$key) {
throw new MessageValidatorException('Cannot get the public key '
. 'from the certificate.');
throw new MessageValidatorException(
'Cannot get the public key from the certificate.'
);
}

// Verify the signature of the message
$content = $message->getStringToSign();
$signature = base64_decode($message->get('Signature'));

if (!openssl_verify($content, $signature, $key, OPENSSL_ALGO_SHA1)) {
throw new MessageValidatorException('The message signature is '
. 'invalid.');
throw new MessageValidatorException(
'The message signature is invalid.'
);
}
}

Expand Down Expand Up @@ -88,7 +100,9 @@ private function validateUrl($url)
// The cert URL must be https, a .pem, and match the following pattern.
static $hostPattern = '/^sns\.[a-zA-Z0-9\-]{3,}\.amazonaws\.com(\.cn)?$/';
$parsed = parse_url($url);
if ($parsed['scheme'] !== 'https'
if (empty($parsed['scheme'])
|| empty($parsed['host'])
|| $parsed['scheme'] !== 'https'
|| substr($url, -4) !== '.pem'
|| !preg_match($hostPattern, $parsed['host'])
) {
Expand Down
5 changes: 4 additions & 1 deletion src/MessageValidatorException.php
@@ -1,7 +1,10 @@
<?php

namespace Aws\Sns;

/**
* Runtime exception thrown by the SNS Message Validator.
*/
class MessageValidatorException extends \RuntimeException {}
class MessageValidatorException extends \RuntimeException
{
}
7 changes: 3 additions & 4 deletions tests/MessageTest.php
@@ -1,7 +1,6 @@
<?php
namespace Aws\Sns\Test;

use Aws\Sns\Message;
namespace Aws\Sns;

/**
* @covers Aws\Sns\Message
Expand Down Expand Up @@ -34,7 +33,7 @@ public function testGetters()
public function testFactorySucceedsWithGoodData()
{
$this->assertInstanceOf(
'Aws\Sns\MessageValidator\Message',
'Aws\Sns\Message',
Message::fromArray($this->messageData)
);
}
Expand Down Expand Up @@ -67,7 +66,7 @@ public function testCanCreateFromRawPost()
stream_wrapper_register('php', __NAMESPACE__ . '\MockPhpStream');

$message = Message::fromRawPostData();
$this->assertInstanceOf('Aws\Sns\MessageValidator\Message', $message);
$this->assertInstanceOf('Aws\Sns\Message', $message);

stream_wrapper_restore("php");
unset($_SERVER['HTTP_X_AMZ_SNS_MESSAGE_TYPE']);
Expand Down
23 changes: 8 additions & 15 deletions tests/MessageValidatorTest.php
@@ -1,8 +1,6 @@
<?php
namespace Aws\Sns\Test;

use Aws\Sns\Message;
use Aws\Sns\MessageValidator;
namespace Aws\Sns;

/**
* @covers \Aws\Sns\MessageValidator
Expand Down Expand Up @@ -45,8 +43,7 @@ public function testValidateFailsWhenCertUrlInvalid()
*/
public function testValidateFailsWhenCannotDeterminePublicKey()
{
$client = $this->getMockClient();
$validator = new MessageValidator($client);
$validator = new MessageValidator($this->getMockClient(''));
$message = new Message([
'SigningCertURL' => self::VALID_CERT_URL
]);
Expand All @@ -63,8 +60,7 @@ public function testValidateFailsWhenMessageIsInvalid()
list($signature, $certificate) = $this->getSignature('foo');
// Create the validator with a mock HTTP client that will respond with
// the certificate
$client = $this->getMockClient(new Psr7\Response(200, [], $certificate));
$validator = new MessageValidator($client);
$validator = new MessageValidator($this->getMockClient($certificate));
$message = new Message([
'SigningCertURL' => self::VALID_CERT_URL,
'Signature' => $signature,
Expand Down Expand Up @@ -93,19 +89,16 @@ public function testValidateSucceedsWhenMessageIsValid()

// Create the validator with a mock HTTP client that will respond with
// the certificate
$client = $this->getMockClient(new Psr7\Response(200, [], $certificate));
$validator = new MessageValidator($client);
$validator = new MessageValidator($this->getMockClient($certificate));

// The message should validate
$this->assertTrue($validator->isValid($message));
}

protected function getMockClient(Psr7\Response $response = null)
protected function getMockClient($responseBody)
{
$response = $response ?: new Psr7\Response(200);

return function () use ($response) {
return \GuzzleHttp\Promise\promise_for($response);
return static function () use ($responseBody) {
return $responseBody;
};
}

Expand All @@ -123,6 +116,6 @@ protected function getSignature($stringToSign)
openssl_pkey_free($keypair);
openssl_x509_free($x509);

return [base64_encode($signature), Psr7\stream_for($certificate)];
return [base64_encode($signature), $certificate];
}
}
3 changes: 2 additions & 1 deletion tests/MockPhpStream.php
@@ -1,5 +1,6 @@
<?php
namespace Aws\Sns\Test;

namespace Aws\Sns;

class MockPhpStream
{
Expand Down

0 comments on commit 03ca320

Please sign in to comment.