/** * @param Request $request * @return int */ public function handleRequest(Request $request) { if (!$request->isMethod('POST')) { return 405; } try { $data = json_decode($request->getContent(), true); $message = Message::fromArray($data); $validator = new MessageValidator(); $validator->isValid($message); } catch (\Exception $e) { return 404; } if (isset($data['Token']) && isset($data['TopicArn'])) { $topicArn = $data['TopicArn']; $token = $data['Token']; $topicEntity = $this->repo->getTopicByArn($topicArn); if ($topicEntity instanceof Topic) { $topicEntity->setToken($token); $this->repo->save($topicEntity); $client = $this->clientFactory->getSnsClient(); $client->confirmSubscription(array('TopicArn' => $topicEntity->getTopicArn(), 'Token' => $topicEntity->getToken())); $this->repo->remove($topicEntity); return 200; } } return 404; }
/** * Validates a message from SNS to ensure that it was delivered by AWS * * @param Message $message The message to validate * * @throws CannotGetPublicKeyFromCertificateException If the certificate cannot be retrieved * @throws CertificateFromUnrecognizedSourceException If the certificate's source cannot be verified * @throws InvalidMessageSignatureException If the message's signature is invalid */ public function validate(Message $message) { // Get the cert's URL and ensure it is from AWS $certUrl = Url::factory($message->get('SigningCertURL')); $this->validateUrl($certUrl); // Get the cert itself and extract the public key $certificate = $this->client->get((string) $certUrl)->send()->getBody(); $publicKey = openssl_get_publickey($certificate); if (!$publicKey) { throw new CannotGetPublicKeyFromCertificateException(); } // Verify the signature of the message $stringToSign = $message->getStringToSign(); $incomingSignature = base64_decode($message->get('Signature')); if (!openssl_verify($stringToSign, $incomingSignature, $publicKey, OPENSSL_ALGO_SHA1)) { throw new InvalidMessageSignatureException(); } }
/** * Validates a message from SNS to ensure that it was delivered by AWS * * @param Message $message The message to validate * * @throws MessageValidatorException If the certificate cannot be * retrieved, if the certificate's source cannot be verified, or if the * message's signature is invalid. */ public function validate(Message $message) { // Get and validate the URL for the certificate. $certUrl = Url::fromString($message->get('SigningCertURL')); $this->validateUrl($certUrl); // Get the cert itself and extract the public key $certificate = $this->client->get((string) $certUrl)->getBody(); $key = openssl_get_publickey($certificate); if (!$key) { 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.'); } }
public function testValidateSucceedsWhenMessageIsValid() { // Create a real message $message = Message::fromArray(array('Message' => 'foo', 'MessageId' => 'bar', 'Timestamp' => time(), 'TopicArn' => 'baz', 'Type' => 'Notification', 'SigningCertURL' => 'https://foo.amazonaws.com/bar', 'Signature' => '')); // Get the signature for a real message list($signature, $certificate) = $this->getSignature($message->getStringToSign()); $message->getData()->set('Signature', $signature); // Create the validator with a mock HTTP client that will respond with the certificate $client = $this->getMockClient(new Response(200, null, $certificate)); $validator = new MessageValidator($client); // The message should validate $this->assertTrue($validator->isValid($message)); }
/** * @param Request $request * @return int */ public function handleRequest(Request $request) { if (!$request->isMethod('POST')) { return 405; } try { $data = json_decode($request->getContent(), true); $message = Message::fromArray($data); $validator = new MessageValidator(); $validator->validate($message); } catch (\Exception $e) { return 404; // not valid message, we return 404 } if (isset($data['Message'])) { $message = json_decode($data['Message'], true); if (!is_null($message)) { if (isset($message['notificationType']) && $message['notificationType'] == self::MESSAGE_TYPE_SUBSCRIPTION_SUCCESS) { return 200; } if (isset($message['notificationType']) && $message['notificationType'] == self::MESSAGE_TYPE_BOUNCE) { foreach ($message['bounce']['bouncedRecipients'] as $bounceRecipient) { $email = $bounceRecipient['emailAddress']; $bounce = $this->repo->findBounceByEmail($email); if ($bounce instanceof Bounce) { $bounce->incrementBounceCounter(); $bounce->setLastTimeBounce(new \DateTime()); $bounce->setPermanent($message['bounce']['bounceType'] == 'Permanent'); } else { $bounce = new Bounce($email, new \DateTime(), 1, $message['bounce']['bounceType'] == 'Permanent'); } $this->repo->save($bounce); } return 200; } } } return 404; }
/** * @dataProvider getDataForStringToSignTest */ public function testBuildsStringToSignCorrectly(array $messageData, $expectedSubject, $expectedStringToSign) { $message = new Message(new Collection($messageData)); $this->assertEquals($expectedSubject, $message->get('Subject')); $this->assertEquals($expectedStringToSign, $message->getStringToSign()); }
/** * Amazon SNS endpoint to receive bounces and complains notified by Amazon SES * */ public function mailBounceAction() { $response = new Response(); $body = $this->request->getRawBody(); $data = @json_decode($body, true); if (!is_array($data)) { return $response; } $message = Message::fromArray($data); $validator = new MessageValidator(); if ($validator->isValid($message)) { $notification = json_decode($message->get('Message'), true); if (is_array($notification)) { do { if (!isset($notification['notificationType'])) { break; } if ($notification['notificationType'] == 'Bounce') { if (!isset($notification['bounce'])) { break; } $bounce = $notification['bounce']; if (!isset($bounce['bouncedRecipients']) || !is_array($bounce['bouncedRecipients'])) { break; } foreach ($bounce['bouncedRecipients'] as $recipient) { $notificationBounce = new NotificationsBounces(); $notificationBounce->email = $recipient['emailAddress']; $notificationBounce->status = $recipient['status']; $notificationBounce->diagnostic = $recipient['diagnosticCode']; $notificationBounce->save(); } } } while (0); } } return $response; }