diff --git a/src/Concerns/GeneratesSnsMessages.php b/src/Concerns/GeneratesSnsMessages.php index 2e90038..7d57dbe 100644 --- a/src/Concerns/GeneratesSnsMessages.php +++ b/src/Concerns/GeneratesSnsMessages.php @@ -8,25 +8,25 @@ trait GeneratesSnsMessages { /** - * Get the private key to sign the request. + * Get the private key to sign the request for SNS. * * @var string */ - protected static $privateKey; + protected static $snsPrivateKey; /** - * The certificate to sign the request. + * The certificate to sign the request for SNS. * * @var string */ - protected static $certificate; + protected static $snsCertificate; /** - * An valid certificate URL for test. + * An valid certificate URL to test SNS. * * @var string */ - public static $validCertUrl = 'https://sns.us-west-2.amazonaws.com/bar.pem'; + public static $snsValidCertUrl = 'https://sns.us-west-2.amazonaws.com/bar.pem'; /** * Initialize the SSL keys and private keys. @@ -35,27 +35,13 @@ trait GeneratesSnsMessages */ protected static function initializeSsl(): void { - self::$privateKey = openssl_pkey_new(); + static::$snsPrivateKey = openssl_pkey_new(); - $csr = openssl_csr_new([], self::$privateKey); + $csr = openssl_csr_new([], static::$snsPrivateKey); - $x509 = openssl_csr_sign($csr, null, self::$privateKey, 1); + $x509 = openssl_csr_sign($csr, null, static::$snsPrivateKey, 1); - openssl_x509_export($x509, self::$certificate); - - // Deprecated in PHP >= 8.0 - // openssl_x509_free($x509); - } - - /** - * Deinitialize the SSL keys. - * - * @return void - */ - protected static function tearDownSsl(): void - { - // Deprecated in PHP >= 8.0 - // openssl_pkey_free(self::$privateKey); + openssl_x509_export($x509, static::$snsCertificate); } /** @@ -66,7 +52,9 @@ protected static function tearDownSsl(): void */ protected function getSignature($stringToSign) { - openssl_sign($stringToSign, $signature, self::$privateKey); + static::initializeSsl(); + + openssl_sign($stringToSign, $signature, static::$snsPrivateKey); return base64_encode($signature); } @@ -91,7 +79,7 @@ protected function getSubscriptionConfirmationPayload(array $custom = []): array 'Timestamp' => now()->toDateTimeString(), 'SignatureVersion' => '1', 'Signature' => true, - 'SigningCertURL' => static::$validCertUrl, + 'SigningCertURL' => static::$snsValidCertUrl, ], $custom); $message['Signature'] = $this->getSignature( @@ -124,7 +112,7 @@ protected function getNotificationPayload(array $payload = [], array $custom = [ 'SignatureVersion' => '1', 'Token' => '2336412f37...', 'Signature' => true, - 'SigningCertURL' => static::$validCertUrl, + 'SigningCertURL' => static::$snsValidCertUrl, 'UnsubscribeURL' => 'https://sns.us-west-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-west-2:123456789012:MyTopic:c9135db0-26c4-47ec-8998-413945fb5a96', ], $custom); @@ -135,6 +123,21 @@ protected function getNotificationPayload(array $payload = [], array $custom = [ return $message; } + /** + * Send the SNS-signed message to the given URL. + * + * @param string $url + * @param array $snsPayload + * @return \Illuminate\Testing\TestResponse + */ + protected function sendSnsMessage($url, array $snsPayload = []) + { + /** @var \Illuminate\Foundation\Testing\Concerns\MakesHttpRequests&\Rennokki\LaravelSnsEvents\Concerns\GeneratesSnsMessages $this */ + return $this->withHeaders($this->getHeadersForMessage($snsPayload)) + ->withHeaders(['X-Sns-Testing-Certificate' => static::$snsCertificate]) + ->json('POST', $url, $snsPayload); + } + /** * Get the right headers for a SNS message. * @@ -144,10 +147,10 @@ protected function getNotificationPayload(array $payload = [], array $custom = [ protected function getHeadersForMessage(array $message): array { return [ - 'X-AMZ-SNS-MESSAGE-TYPE' => $message['Type'], - 'X-AMZ-SNS-MESSAGE-ID' => $message['MessageId'], - 'X-AMZ-SNS-TOPIC-ARN' => $message['TopicArn'], - 'X-AMZ-SNS-SUBSCRIPTION-ARN' => "{$message['TopicArn']}:c9135db0-26c4-47ec-8998-413945fb5a96", + 'X-AMZ-SNS-MESSAGE-TYPE' => $message['Type'] ?? null, + 'X-AMZ-SNS-MESSAGE-ID' => $message['MessageId'] ?? null, + 'X-AMZ-SNS-TOPIC-ARN' => $message['TopicArn'] ?? null, + 'X-AMZ-SNS-SUBSCRIPTION-ARN' => ($message['TopicArn'] ?? null).':c9135db0-26c4-47ec-8998-413945fb5a96', ]; } } diff --git a/src/Concerns/HandlesSns.php b/src/Concerns/HandlesSns.php index f365e6a..d59d325 100644 --- a/src/Concerns/HandlesSns.php +++ b/src/Concerns/HandlesSns.php @@ -6,6 +6,7 @@ use Aws\Sns\MessageValidator; use Exception; use Illuminate\Http\Request; +use Illuminate\Support\Facades\App; trait HandlesSns { @@ -51,6 +52,20 @@ public function snsMessageIsValid(Request $request): bool */ protected function getMessageValidator(Request $request) { + if (App::environment(['testing', 'local'])) { + return new MessageValidator(function ($url) use ($request) { + if ($certificate = $request->sns_certificate) { + return $certificate; + } + + if ($certificate = $request->header('X-Sns-Testing-Certificate')) { + return $certificate; + } + + return $url; + }); + } + return new MessageValidator; } } diff --git a/tests/Controllers/CustomSnsController.php b/tests/Controllers/CustomSnsController.php index bb306d9..bbb6ea4 100644 --- a/tests/Controllers/CustomSnsController.php +++ b/tests/Controllers/CustomSnsController.php @@ -2,7 +2,6 @@ namespace Rennokki\LaravelSnsEvents\Tests\Controllers; -use Aws\Sns\MessageValidator; use Illuminate\Http\Request; use Rennokki\LaravelSnsEvents\Http\Controllers\SnsController; use Rennokki\LaravelSnsEvents\Tests\Events\CustomSnsEvent; @@ -87,17 +86,4 @@ protected function onSubscriptionConfirmation(array $snsMessage, Request $reques { mt_rand(0, 10000); } - - /** - * Get the message validator instance. - * - * @param \Illuminate\Http\Request $request - * @return \Aws\Sns\MessageValidator - */ - protected function getMessageValidator(Request $request) - { - return new MessageValidator(function ($url) use ($request) { - return $request->certificate ?: $url; - }); - } } diff --git a/tests/Controllers/SnsController.php b/tests/Controllers/SnsController.php index 5945f92..42c4e53 100644 --- a/tests/Controllers/SnsController.php +++ b/tests/Controllers/SnsController.php @@ -2,22 +2,9 @@ namespace Rennokki\LaravelSnsEvents\Tests\Controllers; -use Aws\Sns\MessageValidator; -use Illuminate\Http\Request; use Rennokki\LaravelSnsEvents\Http\Controllers\SnsController as BaseSnsController; class SnsController extends BaseSnsController { - /** - * Get the message validator instance. - * - * @param \Illuminate\Http\Request $request - * @return \Aws\Sns\MessageValidator - */ - protected function getMessageValidator(Request $request) - { - return new MessageValidator(function ($url) use ($request) { - return $request->certificate ?: $url; - }); - } + // } diff --git a/tests/EventTest.php b/tests/EventTest.php index 79f9f83..8e2abb9 100644 --- a/tests/EventTest.php +++ b/tests/EventTest.php @@ -14,23 +14,19 @@ public function test_no_event_triggering_on_bad_request() { Event::fake(); - $this->json('GET', route('sns')) - ->assertSee('OK'); + $this->sendSnsMessage(route('sns'))->assertSee('OK'); Event::assertNotDispatched(SnsNotification::class); Event::assertNotDispatched(SnsSubscriptionConfirmation::class); - $this->json('GET', route('sns', ['certificate' => static::$certificate])) - ->assertSee('OK'); + $this->sendSnsMessage(route('sns'))->assertSee('OK'); Event::assertNotDispatched(SnsNotification::class); Event::assertNotDispatched(SnsSubscriptionConfirmation::class); $payload = $this->getSubscriptionConfirmationPayload(); - $this->withHeaders($this->getHeadersForMessage($payload)) - ->json('GET', route('sns', ['certificate' => static::$certificate])) - ->assertSee('OK'); + $this->sendSnsMessage(route('sns'))->assertSee('OK'); Event::assertNotDispatched(SnsNotification::class); Event::assertNotDispatched(SnsSubscriptionConfirmation::class); @@ -42,11 +38,9 @@ public function test_subscription_confirmation() $payload = $this->getSubscriptionConfirmationPayload(); - $this->withHeaders(array_merge($this->getHeadersForMessage($payload), [ - 'x-test-header' => 1, - ])) - ->json('POST', route('sns', ['certificate' => static::$certificate]), $payload) - ->assertSee('OK'); + $this->withHeaders(['x-test-header' => 1]) + ->sendSnsMessage(route('sns'), $payload) + ->assertSee('OK'); Event::assertNotDispatched(SnsNotification::class); @@ -68,11 +62,9 @@ public function test_notification_confirmation() 'sns' => true, ]); - $this->withHeaders(array_merge($this->getHeadersForMessage($payload), [ - 'x-test-header' => 1, - ])) - ->json('POST', route('sns', ['certificate' => static::$certificate]), $payload) - ->assertSee('OK'); + $this->withHeaders(['x-test-header' => 1]) + ->sendSnsMessage(route('sns'), $payload) + ->assertSee('OK'); Event::assertNotDispatched(SnsSubscriptionConfirmation::class); @@ -98,11 +90,9 @@ public function test_custom_controller_confirmation() $payload = $this->getSubscriptionConfirmationPayload(); - $this->withHeaders(array_merge($this->getHeadersForMessage($payload), [ - 'x-test-header' => 1, - ])) - ->json('POST', route('custom-sns', ['test' => 'some-string', 'certificate' => static::$certificate]), $payload) - ->assertSee('OK'); + $this->withHeaders(['x-test-header' => 1]) + ->sendSnsMessage(route('custom-sns', ['test' => 'some-string']), $payload) + ->assertSee('OK'); Event::assertNotDispatched(CustomSnsEvent::class); @@ -128,11 +118,9 @@ public function test_custom_controller_notification() 'sns' => true, ]); - $this->withHeaders(array_merge($this->getHeadersForMessage($payload), [ - 'x-test-header' => 1, - ])) - ->json('POST', route('custom-sns', ['test' => 'some-string', 'certificate' => static::$certificate]), $payload) - ->assertSee('OK'); + $this->withHeaders(['x-test-header' => 1]) + ->sendSnsMessage(route('custom-sns', ['test' => 'some-string']), $payload) + ->assertSee('OK'); Event::assertNotDispatched(CustomSubscriptionConfirmation::class); diff --git a/tests/TestCase.php b/tests/TestCase.php index 316172b..8919dd5 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -9,27 +9,10 @@ class TestCase extends Orchestra { use GeneratesSnsMessages; - /** - * {@inheritdoc} - */ - public static function setUpBeforeClass(): void - { - static::initializeSsl(); - } - - /** - * {@inheritdoc} - */ - public static function tearDownAfterClass(): void - { - static::tearDownSsl(); - } - /** * Get package providers. * * @param \Illuminate\Foundation\Application $app - * * @return array */ protected function getPackageProviders($app)