installed plugin W3 Total Cache version 2.3.2

This commit is contained in:
2023-06-05 11:23:16 +00:00
committed by Gitium
parent d9b3c97e40
commit 51ea2ff21c
2730 changed files with 334913 additions and 0 deletions

View File

@ -0,0 +1,73 @@
---
name: "🐛 Bug Report"
description: Report a bug
title: "(short issue description)"
labels: [bug, needs-triage]
assignees: []
body:
- type: textarea
id: description
attributes:
label: Describe the bug
description: What is the problem? A clear and concise description of the bug.
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected Behavior
description: |
What did you expect to happen?
validations:
required: true
- type: textarea
id: current
attributes:
label: Current Behavior
description: |
What actually happened?
Please include full errors, uncaught exceptions, stack traces, and relevant logs.
If service responses are relevant, please include wire logs.
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Reproduction Steps
description: |
Provide a self-contained, concise snippet of code that can be used to reproduce the issue.
For more complex issues provide a repo with the smallest sample that reproduces the bug.
Avoid including business logic or unrelated code, it makes diagnosis more difficult.
The code sample should be an SSCCE. See http://sscce.org/ for details. In short, please provide a code sample that we can copy/paste, run and reproduce.
validations:
required: true
- type: textarea
id: solution
attributes:
label: Possible Solution
description: |
Suggest a fix/reason for the bug
validations:
required: false
- type: textarea
id: context
attributes:
label: Additional Information/Context
description: |
Anything else that might be relevant for troubleshooting this bug. Providing context helps us come up with a solution that is most useful in the real world.
validations:
required: false
- type: input
id: sdk-version
attributes:
label: SDK version used
validations:
required: true
- type: input
id: environment
attributes:
label: Environment details (OS name and version, etc.)
validations:
required: true

View File

@ -0,0 +1,6 @@
---
blank_issues_enabled: false
contact_links:
- name: 💬 General Question
url: https://github.com/aws/aws-php-sns-message-validator/discussions/categories/q-a
about: Please ask and answer questions as a discussion thread

View File

@ -0,0 +1,23 @@
---
name: "📕 Documentation Issue"
description: Report an issue in the API Reference documentation or Developer Guide
title: "(short issue description)"
labels: [documentation, needs-triage]
assignees: []
body:
- type: textarea
id: description
attributes:
label: Describe the issue
description: A clear and concise description of the issue.
validations:
required: true
- type: textarea
id: links
attributes:
label: Links
description: |
Include links to affected documentation page(s).
validations:
required: true

View File

@ -0,0 +1,59 @@
---
name: 🚀 Feature Request
description: Suggest an idea for this project
title: "(short issue description)"
labels: [feature-request, needs-triage]
assignees: []
body:
- type: textarea
id: description
attributes:
label: Describe the feature
description: A clear and concise description of the feature you are proposing.
validations:
required: true
- type: textarea
id: use-case
attributes:
label: Use Case
description: |
Why do you need this feature? For example: "I'm always frustrated when..."
validations:
required: true
- type: textarea
id: solution
attributes:
label: Proposed Solution
description: |
Suggest how to implement the addition or change. Please include prototype/workaround/sketch/reference implementation.
validations:
required: false
- type: textarea
id: other
attributes:
label: Other Information
description: |
Any alternative solutions or features you considered, a more detailed explanation, stack traces, related issues, links for context, etc.
validations:
required: false
- type: checkboxes
id: ack
attributes:
label: Acknowledgements
options:
- label: I may be able to implement this feature request
required: false
- label: This feature might incur a breaking change
required: false
- type: input
id: sdk-version
attributes:
label: SDK version used
validations:
required: true
- type: input
id: environment
attributes:
label: Environment details (OS name and version, etc.)
validations:
required: true

View File

@ -0,0 +1,6 @@
*Issue #, if available:*
*Description of changes:*
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

View File

@ -0,0 +1,45 @@
name: "Close stale issues"
# Controls when the action will run.
on:
schedule:
- cron: "0 0 * * *"
jobs:
cleanup:
runs-on: ubuntu-latest
name: Stale issue job
steps:
- uses: aws-actions/stale-issue-cleanup@v3
with:
# Setting messages to an empty string will cause the automation to skip
# that category
ancient-issue-message: We have noticed this issue has not recieved attention in 3 years. We will close this issue for now. If you think this is in error, please feel free to comment and reopen the issue.
stale-issue-message: This issue has not recieved a response in 1 week. If you want to keep this issue open, please just leave a comment below and auto-close will be canceled.
# These labels are required
stale-issue-label: closing-soon
exempt-issue-label: no-autoclose
stale-pr-label: no-pr-activity
exempt-pr-label: awaiting-approval
response-requested-label: response-requested
# Don't set closed-for-staleness label to skip closing very old issues
# regardless of label
closed-for-staleness-label: closed-for-staleness
# Issue timing
days-before-stale: 7
days-before-close: 4
days-before-ancient: 1095
# If you don't want to mark a issue as being ancient based on a
# threshold of "upvotes", you can set this here. An "upvote" is
# the total number of +1, heart, hooray, and rocket reactions
# on an issue.
minimum-upvotes-to-exempt: 10
repo-token: ${{ secrets.GITHUB_TOKEN }}
loglevel: DEBUG
# Set dry-run to true to not perform label or close actions.
# dry-run: true

View File

@ -0,0 +1,27 @@
dist: trusty
language: php
php:
- 5.4
- 5.5
- 5.6
- 7.0
- 7.1
- 7.2
- 7.3
- hhvm
- nightly
matrix:
allow_failures:
- php: hhvm
- php: nightly
sudo: false
dist: trusty
install:
- travis_retry composer update --no-interaction --prefer-dist
script: vendor/bin/phpunit

View File

@ -0,0 +1,4 @@
## Code of Conduct
This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
opensource-codeofconduct@amazon.com with any additional questions or comments.

View File

@ -0,0 +1,125 @@
# Contributing Guidelines
Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
documentation, we greatly value feedback and contributions from our community.
Please read through this document before submitting any [issues][] or [pull requests][pull-requests] to ensure we have all the necessary
information to effectively respond to your bug report or contribution.
Jump To:
* [Bug Reports](_#Bug-Reports_)
* [Feature Requests](_#Feature-Requests_)
* [Code Contributions](_#Code-Contributions_)
* [Code of Conduct](_#Code-of-Conduct_)
* [Security issue notifications](_#Security-issue-notifications_)
* [Licensing](_#Licensing_)
## How to contribute
*Before you send us a pull request, please be sure that:*
1. You're working from the latest source on the master branch.
1. You check existing open, and recently closed, pull requests to be sure that
someone else hasn't already addressed the problem.
1. You create an issue before working on a contribution that will take a significant
amount of your time.
*Creating a Pull Request*
1. Fork the repository.
1. In your fork, make your change in a branch that's based on this repo's master branch.
1. Commit the change to your fork, using a clear and descriptive commit message.
1. Create a pull request, answering any questions in the pull request form.
For contributions that will take a significant amount of time, open a new issue to pitch
your idea before you get started. Explain the problem and describe the content you want to
see added to the documentation. Let us know if you'll write it yourself or if you'd like us
to help. We'll discuss your proposal with you and let you know whether we're likely to
accept it.
GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
[creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
## Bug Reports
Bug reports are accepted through the [Issues][] page.
Before Submitting:
* Do a search through the existing issues to make sure it has not already been reported.
If it has, comment your experience or +1 so we prioritize it.
* If possible, upgrade to the latest release of the SDK. It's possible the bug has
already been fixed in the latest version.
Writing the Bug Report:
Please ensure that your bug report has the following:
* A short, descriptive title. Ideally, other community members should be able to get a
good idea of the issue just from reading the title.
* A detailed description of the problem you're experiencing. This should include:
* Expected behavior of the SDK and the actual behavior exhibited.
* Any details of your application environment that may be relevant.
* Debug information, stack trace or logs.
* If you are able to create one, include a Minimal Working Example that reproduces the issue.
* Use Markdown to make the report easier to read; i.e. use code blocks when pasting a
code snippet.
## Feature Requests:
Open an [issue][] with the following:
* A short, descriptive title. Ideally, other community members should be able to get a
good idea of the feature just from reading the title.
* A detailed description of the the proposed feature.
* Why it should be added to the SDK.
* If possible, example code to illustrate how it should work.
* Use Markdown to make the request easier to read;
* If you intend to implement this feature, indicate that you'd like to the issue to be
assigned to you.
## Bug Reports
Bug reports are accepted through the [Issues][] page.
Before Submitting:
* Do a search through the existing issues to make sure it has not already been reported.
If it has, comment your experience or +1 so we prioritize it.
* If possible, upgrade to the latest release of the SDK. It's possible the bug has
already been fixed in the latest version.
Writing the Bug Report:
Please ensure that your bug report has the following:
* A short, descriptive title. Ideally, other community members should be able to get a
good idea of the issue just from reading the title.
* A detailed description of the problem you're experiencing. This should include:
* Expected behavior of the SDK and the actual behavior exhibited.
* Any details of your application environment that may be relevant.
* Debug information, stack trace or logs.
* If you are able to create one, include a Minimal Working Example that reproduces the issue.
* Use Markdown to make the report easier to read; i.e. use code blocks when pasting a
code snippet.
## Code of Conduct
This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
opensource-codeofconduct@amazon.com with any additional questions or comments.
## Security issue notifications
If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
## Licensing
See the [LICENSE](https://github.com/aws/aws-php-sns-message-validator/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes.

View File

@ -0,0 +1,141 @@
# Apache License
Version 2.0, January 2004
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
## 1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1
through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the
License.
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled
by, or are under common control with that entity. For the purposes of this definition, "control" means
(i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract
or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial
ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including but not limited to software
source code, documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form,
including but not limited to compiled object code, generated documentation, and conversions to other media
types.
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License,
as indicated by a copyright notice that is included in or attached to the work (an example is provided in the
Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from)
the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent,
as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not
include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work
and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version of the Work and any
modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to
Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to
submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of
electronic, verbal, or written communication sent to the Licensor or its representatives, including but not
limited to communication on electronic mailing lists, source code control systems, and issue tracking systems
that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been
received by Licensor and subsequently incorporated within the Work.
## 2. Grant of Copyright License.
Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare
Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
## 3. Grant of Patent License.
Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent
license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such
license applies only to those patent claims licensable by such Contributor that are necessarily infringed by
their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such
Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim
or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work
constitutes direct or contributory patent infringement, then any patent licenses granted to You under this
License for that Work shall terminate as of the date such litigation is filed.
## 4. Redistribution.
You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You meet the following conditions:
1. You must give any other recipients of the Work or Derivative Works a copy of this License; and
2. You must cause any modified files to carry prominent notices stating that You changed the files; and
3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent,
trademark, and attribution notices from the Source form of the Work, excluding those notices that do
not pertain to any part of the Derivative Works; and
4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that
You distribute must include a readable copy of the attribution notices contained within such NOTICE
file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed as part of the Derivative Works; within
the Source form or documentation, if provided along with the Derivative Works; or, within a display
generated by the Derivative Works, if and wherever such third-party notices normally appear. The
contents of the NOTICE file are for informational purposes only and do not modify the License. You may
add Your own attribution notices within Derivative Works that You distribute, alongside or as an
addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be
construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide additional or different license
terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative
Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the
conditions stated in this License.
## 5. Submission of Contributions.
Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by
You to the Licensor shall be under the terms and conditions of this License, without any additional terms or
conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate
license agreement you may have executed with Licensor regarding such Contributions.
## 6. Trademarks.
This License does not grant permission to use the trade names, trademarks, service marks, or product names of
the Licensor, except as required for reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
## 7. Disclaimer of Warranty.
Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor
provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT,
MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
## 8. Limitation of Liability.
In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless
required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any
Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential
damages of any character arising as a result of this License or out of the use or inability to use the Work
(including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has been advised of the possibility
of such damages.
## 9. Accepting Warranty or Additional Liability.
While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for,
acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole
responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold
each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@ -0,0 +1,16 @@
# Amazon SNS Message Validator for PHP
<http://aws.amazon.com/php>
Copyright 2010-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License").
You may not use this file except in compliance with the License.
A copy of the License is located at
<http://aws.amazon.com/apache2.0>
or in the "license" file accompanying this file. This file is distributed
on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied. See the License for the specific language governing
permissions and limitations under the License.

View File

@ -0,0 +1,179 @@
# Amazon SNS Message Validator for PHP
[![@awsforphp on Twitter](http://img.shields.io/badge/twitter-%40awsforphp-blue.svg?style=flat)](https://twitter.com/awsforphp)
[![Total Downloads](https://img.shields.io/packagist/dt/aws/aws-php-sns-message-validator.svg?style=flat)](https://packagist.org/packages/aws/aws-php-sns-message-validator)
[![Build Status](https://img.shields.io/travis/aws/aws-php-sns-message-validator.svg?style=flat)](https://travis-ci.org/aws/aws-php-sns-message-validator)
[![Apache 2 License](https://img.shields.io/packagist/l/aws/aws-php-sns-message-validator.svg?style=flat)](http://aws.amazon.com/apache-2-0/)
The **Amazon SNS Message Validator for PHP** library allows you to validate that
incoming HTTP(S) POST messages are valid Amazon SNS notifications. This library
is standalone and does not depend on the AWS SDK for PHP or Guzzle; however, it
does require PHP 5.4+ and that the OpenSSL PHP extension is installed.
Jump To:
* [Basic Usage](_#Basic-Usage_)
* [Installation](_#Installation_)
* [About Amazon SNS](_#About-Amazon-SNS_)
* [Handling Messages](_#Handling-Messages_)
* [Testing Locally](_#Testing-Locally_)
* [Contributing](_#Contributing_)
## Basic Usage
To validate a message, you can instantiate a `Message` object from the POST
data using the `Message::fromRawPostData`. This reads the raw POST data from
the [`php://input` stream][php-input], decodes the JSON data, and validates
the message's type and structure.
Next, you must create an instance of `MessageValidator`, and then use either
the `isValid()` or `validate()`, methods to validate the message. The
message validator checks the `SigningCertURL`, `SignatureVersion`, and
`Signature` to make sure they are valid and consistent with the message data.
```php
<?php
require 'vendor/autoload.php';
use Aws\Sns\Message;
use Aws\Sns\MessageValidator;
$message = Message::fromRawPostData();
// Validate the message
$validator = new MessageValidator();
if ($validator->isValid($message)) {
// do something with the message
}
```
## Installation
The SNS Message Validator can be installed via [Composer][].
$ composer require aws/aws-php-sns-message-validator
## Getting Help
Please use these community resources for getting help. We use the GitHub issues for tracking bugs and feature requests and have limited bandwidth to address them.
* Ask a question on [StackOverflow](https://stackoverflow.com/) and tag it with [`aws-php-sdk`](http://stackoverflow.com/questions/tagged/aws-php-sdk)
* Come join the AWS SDK for PHP [gitter](https://gitter.im/aws/aws-sdk-php)
* Open a support ticket with [AWS Support](https://console.aws.amazon.com/support/home/)
* If it turns out that you may have found a bug, please [open an issue](https://github.com/aws/aws-php-sns-message-validator/issues/new/choose)
## About Amazon SNS
[Amazon Simple Notification Service (Amazon SNS)][sns] is a fast, fully-managed,
push messaging service. Amazon SNS can deliver messages to email, mobile devices
(i.e., SMS; iOS, Android and FireOS push notifications), Amazon SQS queues,and
— of course — HTTP/HTTPS endpoints.
With Amazon SNS, you can setup topics to publish custom messages to subscribed
endpoints. However, SNS messages are used by many of the other AWS services to
communicate information asynchronously about your AWS resources. Some examples
include:
* Configuring Amazon Glacier to notify you when a retrieval job is complete.
* Configuring AWS CloudTrail to notify you when a new log file has been written.
* Configuring Amazon Elastic Transcoder to notify you when a transcoding job
changes status (e.g., from "Progressing" to "Complete")
Though you can certainly subscribe your email address to receive SNS messages
from service events like these, your inbox would fill up rather quickly. There
is great power, however, in being able to subscribe an HTTP/HTTPS endpoint to
receive the messages. This allows you to program webhooks for your applications
to easily respond to various events.
## Handling Messages
### Confirming a Subscription to a Topic
In order to handle a `SubscriptionConfirmation` message, you must use the
`SubscribeURL` value in the incoming message:
```php
use Aws\Sns\Message;
use Aws\Sns\MessageValidator;
use Aws\Sns\Exception\InvalidSnsMessageException;
// Instantiate the Message and Validator
$message = Message::fromRawPostData();
$validator = new MessageValidator();
// Validate the message and log errors if invalid.
try {
$validator->validate($message);
} catch (InvalidSnsMessageException $e) {
// Pretend we're not here if the message is invalid.
http_response_code(404);
error_log('SNS Message Validation Error: ' . $e->getMessage());
die();
}
// Check the type of the message and handle the subscription.
if ($message['Type'] === 'SubscriptionConfirmation') {
// Confirm the subscription by sending a GET request to the SubscribeURL
file_get_contents($message['SubscribeURL']);
}
```
### Receiving a Notification
To receive a notification, use the same code as the preceding example, but
check for the `Notification` message type.
```php
if ($message['Type'] === 'Notification') {
// Do whatever you want with the message body and data.
echo $message['MessageId'] . ': ' . $message['Message'] . "\n";
}
```
The message body will be a string, and will hold whatever data was published
to the SNS topic.
### Unsubscribing
Unsubscribing looks the same as subscribing, except the message type will be
`UnsubscribeConfirmation`.
```php
if ($message['Type'] === 'UnsubscribeConfirmation') {
// Unsubscribed in error? You can resubscribe by visiting the endpoint
// provided as the message's SubscribeURL field.
file_get_contents($message['SubscribeURL']);
}
```
## Testing Locally
One challenge of using webhooks in a web application is testing the integration
with the service. Testing integrations with SNS notifications can be fairly easy
using tools like [ngrok][] and [PHP's built-in webserver][php-server]. One of
our blog posts, [*Testing Webhooks Locally for Amazon SNS*][blogpost], illustrates
a good technique for testing.
> **NOTE:** The code samples in the blog post are specific to the message
> validator in Version 2 of the SDK, but can be easily adapted to using this
> version.
### Special Thank You
A special thanks goes out to [Julian Vidal][] who helped create the [initial
implementation][] in Version 2 of the [AWS SDK for PHP][].
[php-input]: http://php.net/manual/en/wrappers.php.php#wrappers.php.input
[composer]: https://getcomposer.org/
[source code]: https://github.com/aws/aws-php-sns-message-validator/archive/master.zip
[sns]: http://aws.amazon.com/sns/
[ngrok]: https://ngrok.com/
[php-server]: http://www.php.net/manual/en/features.commandline.webserver.php
[blogpost]: http://blogs.aws.amazon.com/php/post/Tx2CO24DVG9CAK0/Testing-Webhooks-Locally-for-Amazon-SNS
[Julian Vidal]: https://github.com/poisa
[initial implementation]: https://github.com/aws/aws-sdk-php/tree/2.8/src/Aws/Sns/MessageValidator
[AWS SDK for PHP]: https://github.com/aws/aws-sdk-php
## Contributing
We work hard to provide a high-quality and useful SDK for our AWS services, and we greatly value feedback and contributions from our community. Please review our [contributing guidelines](./CONTRIBUTING.md) before submitting any issues or pull requests to ensure we have all the necessary information to effectively respond to your bug report or contribution.

View File

@ -0,0 +1,37 @@
{
"name": "aws/aws-php-sns-message-validator",
"homepage": "http://aws.amazon.com/sdkforphp",
"description": "Amazon SNS message validation for PHP",
"keywords": ["aws","amazon","sdk","sns","message","webhooks","cloud"],
"type": "library",
"license": "Apache-2.0",
"authors": [
{
"name": "Amazon Web Services",
"homepage": "http://aws.amazon.com"
}
],
"support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sns-message-validator/issues"
},
"require": {
"php": ">=5.4",
"ext-openssl": "*",
"psr/http-message": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0",
"squizlabs/php_codesniffer": "^2.3",
"guzzlehttp/psr7": "^1.4"
},
"autoload": {
"psr-4": { "Aws\\Sns\\": "src/" }
},
"autoload-dev": {
"psr-4": { "Aws\\Sns\\": "tests/" }
},
"scripts": {
"test": "phpunit"
}
}

View File

@ -0,0 +1,15 @@
<?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>
<filter>
<whitelist>
<directory suffix=".php">src/</directory>
</whitelist>
</filter>
</phpunit>

View File

@ -0,0 +1,9 @@
<?php
namespace Aws\Sns\Exception;
/**
* Runtime exception thrown by the SNS Message Validator.
*/
class InvalidSnsMessageException extends \RuntimeException
{
}

View File

@ -0,0 +1,161 @@
<?php
namespace Aws\Sns;
use Psr\Http\Message\RequestInterface;
/**
* Represents an SNS message received over http(s).
*/
class Message implements \ArrayAccess, \IteratorAggregate
{
private static $requiredKeys = [
'Message',
'MessageId',
'Timestamp',
'TopicArn',
'Type',
'Signature',
['SigningCertURL', 'SigningCertUrl'],
'SignatureVersion',
];
private static $subscribeKeys = [
['SubscribeURL', 'SubscribeUrl'],
'Token'
];
/** @var array The message data */
private $data;
/**
* Creates a Message object from the raw POST data
*
* @return Message
* @throws \RuntimeException If the POST data is absent, or not a valid JSON document
*/
public static function fromRawPostData()
{
// Make sure the SNS-provided header exists.
if (!isset($_SERVER['HTTP_X_AMZ_SNS_MESSAGE_TYPE'])) {
throw new \RuntimeException('SNS message type header not provided.');
}
// Read the raw POST data and JSON-decode it into a message.
return self::fromJsonString(file_get_contents('php://input'));
}
/**
* Creates a Message object from a PSR-7 Request or ServerRequest object.
*
* @param RequestInterface $request
* @return Message
*/
public static function fromPsrRequest(RequestInterface $request)
{
return self::fromJsonString($request->getBody());
}
/**
* Creates a Message object from a JSON-decodable string.
*
* @param string $requestBody
* @return Message
*/
public static function fromJsonString($requestBody)
{
$data = json_decode($requestBody, true);
if (JSON_ERROR_NONE !== json_last_error() || !is_array($data)) {
throw new \RuntimeException('Invalid POST data.');
}
return new Message($data);
}
/**
* Creates a Message object from an array of raw message data.
*
* @param array $data The message data.
*
* @throws \InvalidArgumentException If a valid type is not provided or
* there are other required keys missing.
*/
public function __construct(array $data)
{
// Ensure that all the required keys for the message's type are present.
$this->validateRequiredKeys($data, self::$requiredKeys);
if ($data['Type'] === 'SubscriptionConfirmation'
|| $data['Type'] === 'UnsubscribeConfirmation'
) {
$this->validateRequiredKeys($data, self::$subscribeKeys);
}
$this->data = $data;
}
#[\ReturnTypeWillChange]
public function getIterator()
{
return new \ArrayIterator($this->data);
}
#[\ReturnTypeWillChange]
public function offsetExists($key)
{
return isset($this->data[$key]);
}
#[\ReturnTypeWillChange]
public function offsetGet($key)
{
return isset($this->data[$key]) ? $this->data[$key] : null;
}
#[\ReturnTypeWillChange]
public function offsetSet($key, $value)
{
$this->data[$key] = $value;
}
#[\ReturnTypeWillChange]
public function offsetUnset($key)
{
unset($this->data[$key]);
}
/**
* Get all the message data as a plain array.
*
* @return array
*/
public function toArray()
{
return $this->data;
}
private function validateRequiredKeys(array $data, array $keys)
{
foreach ($keys as $key) {
$keyIsArray = is_array($key);
if (!$keyIsArray) {
$found = isset($data[$key]);
} else {
$found = false;
foreach ($key as $keyOption) {
if (isset($data[$keyOption])) {
$found = true;
break;
}
}
}
if (!$found) {
if ($keyIsArray) {
$key = $key[0];
}
throw new \InvalidArgumentException(
"\"{$key}\" is required to verify the SNS Message."
);
}
}
}
}

View File

@ -0,0 +1,195 @@
<?php
namespace Aws\Sns;
use Aws\Sns\Exception\InvalidSnsMessageException;
/**
* Uses openssl to verify SNS messages to ensure that they were sent by AWS.
*/
class MessageValidator
{
const SIGNATURE_VERSION_1 = '1';
const SIGNATURE_VERSION_2 = '2';
/**
* @var callable Callable used to download the certificate content.
*/
private $certClient;
/** @var string */
private $hostPattern;
/**
* @var string A pattern that will match all regional SNS endpoints, e.g.:
* - sns.<region>.amazonaws.com (AWS)
* - sns.us-gov-west-1.amazonaws.com (AWS GovCloud)
* - sns.cn-north-1.amazonaws.com.cn (AWS China)
*/
private static $defaultHostPattern
= '/^sns\.[a-zA-Z0-9\-]{3,}\.amazonaws\.com(\.cn)?$/';
private static function isLambdaStyle(Message $message)
{
return isset($message['SigningCertUrl']);
}
private static function convertLambdaMessage(Message $lambdaMessage)
{
$keyReplacements = [
'SigningCertUrl' => 'SigningCertURL',
'SubscribeUrl' => 'SubscribeURL',
'UnsubscribeUrl' => 'UnsubscribeURL',
];
$message = clone $lambdaMessage;
foreach ($keyReplacements as $lambdaKey => $canonicalKey) {
if (isset($message[$lambdaKey])) {
$message[$canonicalKey] = $message[$lambdaKey];
unset($message[$lambdaKey]);
}
}
return $message;
}
/**
* Constructs the Message Validator object and ensures that openssl is
* installed.
*
* @param callable $certClient Callable used to download the certificate.
* Should have the following function signature:
* `function (string $certUrl) : string|false $certContent`
* @param string $hostNamePattern
*/
public function __construct(
callable $certClient = null,
$hostNamePattern = ''
) {
$this->certClient = $certClient ?: function($certUrl) {
return @ file_get_contents($certUrl);
};
$this->hostPattern = $hostNamePattern ?: self::$defaultHostPattern;
}
/**
* Validates a message from SNS to ensure that it was delivered by AWS.
*
* @param Message $message Message to validate.
*
* @throws InvalidSnsMessageException If the cert cannot be retrieved or its
* source verified, or the message
* signature is invalid.
*/
public function validate(Message $message)
{
if (self::isLambdaStyle($message)) {
$message = self::convertLambdaMessage($message);
}
// Get the certificate.
$this->validateUrl($message['SigningCertURL']);
$certificate = call_user_func($this->certClient, $message['SigningCertURL']);
if ($certificate === false) {
throw new InvalidSnsMessageException(
"Cannot get the certificate from \"{$message['SigningCertURL']}\"."
);
}
// Extract the public key.
$key = openssl_get_publickey($certificate);
if (!$key) {
throw new InvalidSnsMessageException(
'Cannot get the public key from the certificate.'
);
}
// Verify the signature of the message.
$content = $this->getStringToSign($message);
$signature = base64_decode($message['Signature']);
$algo = ($message['SignatureVersion'] === self::SIGNATURE_VERSION_1 ? OPENSSL_ALGO_SHA1 : OPENSSL_ALGO_SHA256);
if (openssl_verify($content, $signature, $key, $algo) !== 1) {
throw new InvalidSnsMessageException(
'The message signature is invalid.'
);
}
}
/**
* Determines if a message is valid and that is was delivered by AWS. This
* method does not throw exceptions and returns a simple boolean value.
*
* @param Message $message The message to validate
*
* @return bool
*/
public function isValid(Message $message)
{
try {
$this->validate($message);
return true;
} catch (InvalidSnsMessageException $e) {
return false;
}
}
/**
* Builds string-to-sign according to the SNS message spec.
*
* @param Message $message Message for which to build the string-to-sign.
*
* @return string
* @link http://docs.aws.amazon.com/sns/latest/gsg/SendMessageToHttp.verify.signature.html
*/
public function getStringToSign(Message $message)
{
static $signableKeys = [
'Message',
'MessageId',
'Subject',
'SubscribeURL',
'Timestamp',
'Token',
'TopicArn',
'Type',
];
if ($message['SignatureVersion'] !== self::SIGNATURE_VERSION_1
&& $message['SignatureVersion'] !== self::SIGNATURE_VERSION_2) {
throw new InvalidSnsMessageException(
"The SignatureVersion \"{$message['SignatureVersion']}\" is not supported."
);
}
$stringToSign = '';
foreach ($signableKeys as $key) {
if (isset($message[$key])) {
$stringToSign .= "{$key}\n{$message[$key]}\n";
}
}
return $stringToSign;
}
/**
* Ensures that the URL of the certificate is one belonging to AWS, and not
* just something from the amazonaws domain, which could include S3 buckets.
*
* @param string $url Certificate URL
*
* @throws InvalidSnsMessageException if the cert url is invalid.
*/
private function validateUrl($url)
{
$parsed = parse_url($url);
if (empty($parsed['scheme'])
|| empty($parsed['host'])
|| $parsed['scheme'] !== 'https'
|| substr($url, -4) !== '.pem'
|| !preg_match($this->hostPattern, $parsed['host'])
) {
throw new InvalidSnsMessageException(
'The certificate is located on an invalid domain.'
);
}
}
}

View File

@ -0,0 +1,229 @@
<?php
namespace Aws\Sns;
/**
* @covers Aws\Sns\MessageValidator
* @covers Aws\Sns\Message
*/
class FunctionalValidationsTest extends \PHPUnit_Framework_TestCase
{
private static $certificate =
'-----BEGIN CERTIFICATE-----
MIIF1zCCBL+gAwIBAgIQB9pYWG3Mi7xej22g9pobJTANBgkqhkiG9w0BAQsFADBG
MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRUwEwYDVQQLEwxTZXJ2ZXIg
Q0EgMUIxDzANBgNVBAMTBkFtYXpvbjAeFw0yMTA5MDcwMDAwMDBaFw0yMjA4MTcy
MzU5NTlaMBwxGjAYBgNVBAMTEXNucy5hbWF6b25hd3MuY29tMIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAutFqueT3XgP13udzxE6UpbdjOtVO5DwoMpSM
iDNMnGzF1TYH5/R2LPUOBeTB0SkKnR4kpNcUZhicpGD4aKciz/GEZ6wu65xncfT9
H/KBOQwoXYTuClHwp6fYpGzcGFaFoEYMnijL/o4qmTSd+ukglQUgKpsDw4ofw6rU
m2CttJo+GQSNQ9NfGR1h/0J+zsApkeSYrXRx5wNlu87z8os1C/6PBrUHwt3xXeaf
Xzfwut8aRRYsS8BySOA9DAgLfNHlfdQCjKPXKrG/ussgReyWD6n/HH+j7Uha3xos
TzQqJifcxlTq6MxWdPR6fDaJNvqw6DOE7UjUNxHguXHlVfxhlQIDAQABo4IC6TCC
AuUwHwYDVR0jBBgwFoAUWaRmBlKge5WSPKOUByeWdFv5PdAwHQYDVR0OBBYEFAqz
C+vyouneE7mWWLbi9i0UsWUbMBwGA1UdEQQVMBOCEXNucy5hbWF6b25hd3MuY29t
MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
OwYDVR0fBDQwMjAwoC6gLIYqaHR0cDovL2NybC5zY2ExYi5hbWF6b250cnVzdC5j
b20vc2NhMWIuY3JsMBMGA1UdIAQMMAowCAYGZ4EMAQIBMHUGCCsGAQUFBwEBBGkw
ZzAtBggrBgEFBQcwAYYhaHR0cDovL29jc3Auc2NhMWIuYW1hem9udHJ1c3QuY29t
MDYGCCsGAQUFBzAChipodHRwOi8vY3J0LnNjYTFiLmFtYXpvbnRydXN0LmNvbS9z
Y2ExYi5jcnQwDAYDVR0TAQH/BAIwADCCAX0GCisGAQQB1nkCBAIEggFtBIIBaQFn
AHYAKXm+8J45OSHwVnOfY6V35b5XfZxgCvj5TV0mXCVdx4QAAAF7vfDVkQAABAMA
RzBFAiEA2XfHuy36aqRFiaL8c3md2mH451go8707+fRE0pEdSRACIE/g5FXTUXUZ
PFcmOhm9TZ+uMY1i4CIQ/CKVWln6C3t+AHYAUaOw9f0BeZxWbbg3eI8MpHrMGyfL
956IQpoN/tSLBeUAAAF7vfDVjAAABAMARzBFAiBF1MhhFP0+FQt3daDFfMYoWwnr
muTInrjNpwfzlvQBugIhAPYadFzr+LaxSJoiZEbEHBvTts7bT0M3eCQONA2O7w6n
AHUAQcjKsd8iRkoQxqE6CUKHXk4xixsD6+tLx2jwkGKWBvYAAAF7vfDVdAAABAMA
RjBEAiAtPapmFAuA71ih4NoSd5hJelzAltNQpxDMcDfDyHyU8gIgWxmaa6+2KbBu
9xdv379zvnJACFR7jc+4asl08Dn4aagwDQYJKoZIhvcNAQELBQADggEBAA54QX0u
oFWXfMmv02CGZv4NWo5TapyeeixQ2kKpZHRdVZjxZrw+hoF6HD7P3kGjH8ztyJll
tDxB0qgMltbPhQdScwhA6iTgoaBYqEUC/VHKd4PmmPT6yIsM36NBZVmkGlzl5uNo
/dBgBaG0SsVJnhr5zro3c2quC7n6fVGEZhf/UgQwRnnvThnvbNKguglDMq4uEqv8
njKyleht+glkcmXO0m9qLKt6BOS0amy6U2GlAwRn0Wx02ndJtnRCSC6kPuRWK/SQ
FEjB7gCK4hdKaAOuWdZpI55vF6ifOeM8toC3g7ofO8qLTnJupAG+ZitY5J3cvHWr
HqOUdKigPDHYLRo=
-----END CERTIFICATE-----';
public function getHttpFixtures()
{
return [
[
[
'Type' => "Notification",
'MessageId' => "792cda85-518f-5dd3-9163-81d851212f3a",
'TopicArn' => "arn:aws:sns:us-east-2:295079676684:publish-and-verify-892f85fe-4836-424d-8188-ab85bef0f362",
'Message' => "Hello world",
'Timestamp' => "2022-07-28T21:23:58.317Z",
'SignatureVersion' => "1",
'Signature' => "ghtf+deOBAzHJJZ6s6CdRLfTQAlcGzq9naoFM1wi0CJiq//uVRuZnamrkWNF0fhouMFvuLVRwcz8PZLUMSfnmd5VpdTKpTyiKmy1qJAZXma0w+yi7G+I33hD1Jyk1Nbym2n0kqp3fVu2aoooiN2ZeLAT2bH0/BtjLSfN1yAOKNoprco4qV9gGUZinXJdj9a1YdNhDR2jKi33ldlsVtEXAtiaDklGEk7DgRKX38GerBPiLg3FdtgY6KC7cdeGpU/dGK+4hjc83Ive1HoFkAwqhpgInM2sMytBosoiXfCmOKmU4xeGD0gHDNZTlJUJQDlzw8Eag0H9f/5zXF9d3uy0YQ==",
'SigningCertURL' => "https://sns.us-east-2.amazonaws.com/SimpleNotificationService-7ff5318490ec183fbaddaa2a969abfda.pem",
'UnsubscribeURL' => "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:295079676684:publish-and-verify-892f85fe-4836-424d-8188-ab85bef0f362:2296bc94-7992-4be1-b15f-b97229b5c1d8",
]
],
[
[
'Type' => "Notification",
'MessageId' => "17dea24b-55c2-540b-8362-f916557af765",
'TopicArn' => "arn:aws:sns:us-east-2:295079676684:publish-and-verify-62674b1d-4295-426b-88e7-5fb75652a04e",
'Message' => "Hello world",
'Timestamp' => "2022-07-28T21:24:08.324Z",
'SignatureVersion' => "2",
'Signature' => "CXVqp9PfZAL+4JHS3Zxo1PFbQsvnOjvmYhtIf17TWpwc+iIVas8kZ8GopuzVzVMdatE7rCl/O4P91Zp05Dwz8lk8dLhfp8gSu3Njlzxlyrmzo9x3va3Jb7zFnedgS2GKnZWHGBdwTXho+TosNUE+3e10OMSlwN5XGDwX7+R3WL+rn+AXmFAqp3alg27sYa55h1dLE9cGszGPjScPdtF3BmZsUDMx9wSdNKsCk+vSvE8yBjnCmUl7laSFj3LzPVrlSwgNYCF3kYnNAkah7NplK4SFhJYLwS0HCVCQJKa8rVbQLf9cBTu60U402mrgy0bN8xWoyimzbYbrOMJjalqkUg==",
'SigningCertURL' => "https://sns.us-east-2.amazonaws.com/SimpleNotificationService-7ff5318490ec183fbaddaa2a969abfda.pem",
'UnsubscribeURL' => "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:295079676684:publish-and-verify-62674b1d-4295-426b-88e7-5fb75652a04e:ad7d16e3-0a7c-46aa-b23e-ffaf02250cbe",
]
],
[
[
'Type' => "Notification",
'MessageId' => "11405cc3-9ac7-56d5-b45d-079e8f7a8edf",
'TopicArn' => "arn:aws:sns:us-east-2:295079676684:publish-and-verify-6e11fed2-fcdf-4c52-9dc4-36ef43f37f84",
'Subject' => "Hello world",
'Message' => "Hello world",
'Timestamp' => "2022-07-28T22:53:49.654Z",
'SignatureVersion' => "1",
'Signature' => "AItkS26d8yvnIKJevdirIPW7eM/yKbZy3/CF2EreCHmXWB3etWaV5Fb7SYpGABMpugpDZzNyGY1wCVWaopDoQ+7Q/kI2TpDu8bw1eExbi8U3kduvc/2m2fIrI4gDEY8/v3nzoLcr8pPodqMzrX6SzQou4klfaqbNK+rFmH0LVf2Q1VyOROODoSXmo4jg2Yu12jfxccBl96Drr/ihq4MJ4OcrWh6UzXXlVYjJHx2Ui4anNwNEb+Z4C2CAF1DjQUbhDtaoajDBPY+4d9C1OwbqwQpXsd6tyVcI9nFyEsVK8lfnAV+/3GZQcdXHbIUYBRGcBa4X5TlWJku5nDH2ERtHHw==",
'SigningCertURL' => "https://sns.us-east-2.amazonaws.com/SimpleNotificationService-7ff5318490ec183fbaddaa2a969abfda.pem",
'UnsubscribeURL' => "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:295079676684:publish-and-verify-6e11fed2-fcdf-4c52-9dc4-36ef43f37f84:adb318c3-2234-4c56-905d-c324cf0df874",
]
],
[
[
'Type' => "Notification",
'MessageId' => "4504e649-d933-5aa9-8199-bd14ccf05f0b",
'TopicArn' => "arn:aws:sns:us-east-2:295079676684:publish-and-verify-530b26da-0687-4fe4-9f71-780bad3181e2",
'Subject' => "Hello world",
'Message' => "Hello world",
'Timestamp' => "2022-07-28T22:53:55.086Z",
'SignatureVersion' => "2",
'Signature' => "cETcSvmmkt+My05qCLKexyl0+RyG83mSryKPqTfS+tYcxDJWVcjPJAr+qdpElzVaBl1aTGYVWMY64i9JqZ/JES8pylNj8LGvdhuNQKO59/WCoIimZAsNhn0xEgOeeDU+W/0BU4sdpCGMNjo0S/FuIiWaRe4E0YWRVrxeQevaQ70euDdfWgd5v1eCKQz8b367b9XBmMztL/CWUFI6YaKK/MV21eyvJe3Y7CtVYiOKEYiAZnAEkynK7gUGO5TsgDjGNYhj6U3xYsWgI03bmioSl7kdFSUj+AZ7ugas5fghqxgoDsdfqsjMYKRm5KKHQWsgzI619yIzpNKUiSMHxdZXpQ==",
'SigningCertURL' => "https://sns.us-east-2.amazonaws.com/SimpleNotificationService-7ff5318490ec183fbaddaa2a969abfda.pem",
'UnsubscribeURL' => "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:295079676684:publish-and-verify-530b26da-0687-4fe4-9f71-780bad3181e2:db0ad2ad-03d1-48ca-a5da-51f317800a57"
]
]
];
}
public function getLambdaFixtures()
{
return [
[
[
'Type' => "Notification",
'MessageId' => "792cda85-518f-5dd3-9163-81d851212f3a",
'TopicArn' => "arn:aws:sns:us-east-2:295079676684:publish-and-verify-892f85fe-4836-424d-8188-ab85bef0f362",
'Message' => "Hello world",
'Timestamp' => "2022-07-28T21:23:58.317Z",
'SignatureVersion' => "1",
'Signature' => "ghtf+deOBAzHJJZ6s6CdRLfTQAlcGzq9naoFM1wi0CJiq//uVRuZnamrkWNF0fhouMFvuLVRwcz8PZLUMSfnmd5VpdTKpTyiKmy1qJAZXma0w+yi7G+I33hD1Jyk1Nbym2n0kqp3fVu2aoooiN2ZeLAT2bH0/BtjLSfN1yAOKNoprco4qV9gGUZinXJdj9a1YdNhDR2jKi33ldlsVtEXAtiaDklGEk7DgRKX38GerBPiLg3FdtgY6KC7cdeGpU/dGK+4hjc83Ive1HoFkAwqhpgInM2sMytBosoiXfCmOKmU4xeGD0gHDNZTlJUJQDlzw8Eag0H9f/5zXF9d3uy0YQ==",
'SigningCertUrl' => "https://sns.us-east-2.amazonaws.com/SimpleNotificationService-7ff5318490ec183fbaddaa2a969abfda.pem",
'UnsubscribeUrl' => "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:295079676684:publish-and-verify-892f85fe-4836-424d-8188-ab85bef0f362:2296bc94-7992-4be1-b15f-b97229b5c1d8",
]
],
[
[
'Type' => "Notification",
'MessageId' => "17dea24b-55c2-540b-8362-f916557af765",
'TopicArn' => "arn:aws:sns:us-east-2:295079676684:publish-and-verify-62674b1d-4295-426b-88e7-5fb75652a04e",
'Message' => "Hello world",
'Timestamp' => "2022-07-28T21:24:08.324Z",
'SignatureVersion' => "2",
'Signature' => "CXVqp9PfZAL+4JHS3Zxo1PFbQsvnOjvmYhtIf17TWpwc+iIVas8kZ8GopuzVzVMdatE7rCl/O4P91Zp05Dwz8lk8dLhfp8gSu3Njlzxlyrmzo9x3va3Jb7zFnedgS2GKnZWHGBdwTXho+TosNUE+3e10OMSlwN5XGDwX7+R3WL+rn+AXmFAqp3alg27sYa55h1dLE9cGszGPjScPdtF3BmZsUDMx9wSdNKsCk+vSvE8yBjnCmUl7laSFj3LzPVrlSwgNYCF3kYnNAkah7NplK4SFhJYLwS0HCVCQJKa8rVbQLf9cBTu60U402mrgy0bN8xWoyimzbYbrOMJjalqkUg==",
'SigningCertUrl' => "https://sns.us-east-2.amazonaws.com/SimpleNotificationService-7ff5318490ec183fbaddaa2a969abfda.pem",
'UnsubscribeUrl' => "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:295079676684:publish-and-verify-62674b1d-4295-426b-88e7-5fb75652a04e:ad7d16e3-0a7c-46aa-b23e-ffaf02250cbe",
]
],
[
[
'Type' => "Notification",
'MessageId' => "792cda85-518f-5dd3-9163-81d851212f3a",
'TopicArn' => "arn:aws:sns:us-east-2:295079676684:publish-and-verify-892f85fe-4836-424d-8188-ab85bef0f362",
'Subject' => null,
'Message' => "Hello world",
'Timestamp' => "2022-07-28T21:23:58.317Z",
'SignatureVersion' => "1",
'Signature' => "ghtf+deOBAzHJJZ6s6CdRLfTQAlcGzq9naoFM1wi0CJiq//uVRuZnamrkWNF0fhouMFvuLVRwcz8PZLUMSfnmd5VpdTKpTyiKmy1qJAZXma0w+yi7G+I33hD1Jyk1Nbym2n0kqp3fVu2aoooiN2ZeLAT2bH0/BtjLSfN1yAOKNoprco4qV9gGUZinXJdj9a1YdNhDR2jKi33ldlsVtEXAtiaDklGEk7DgRKX38GerBPiLg3FdtgY6KC7cdeGpU/dGK+4hjc83Ive1HoFkAwqhpgInM2sMytBosoiXfCmOKmU4xeGD0gHDNZTlJUJQDlzw8Eag0H9f/5zXF9d3uy0YQ==",
'SigningCertUrl' => "https://sns.us-east-2.amazonaws.com/SimpleNotificationService-7ff5318490ec183fbaddaa2a969abfda.pem",
'UnsubscribeUrl' => "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:295079676684:publish-and-verify-892f85fe-4836-424d-8188-ab85bef0f362:2296bc94-7992-4be1-b15f-b97229b5c1d8",
]
],
[
[
'Type' => "Notification",
'MessageId' => "17dea24b-55c2-540b-8362-f916557af765",
'TopicArn' => "arn:aws:sns:us-east-2:295079676684:publish-and-verify-62674b1d-4295-426b-88e7-5fb75652a04e",
'Subject' => null,
'Message' => "Hello world",
'Timestamp' => "2022-07-28T21:24:08.324Z",
'SignatureVersion' => "2",
'Signature' => "CXVqp9PfZAL+4JHS3Zxo1PFbQsvnOjvmYhtIf17TWpwc+iIVas8kZ8GopuzVzVMdatE7rCl/O4P91Zp05Dwz8lk8dLhfp8gSu3Njlzxlyrmzo9x3va3Jb7zFnedgS2GKnZWHGBdwTXho+TosNUE+3e10OMSlwN5XGDwX7+R3WL+rn+AXmFAqp3alg27sYa55h1dLE9cGszGPjScPdtF3BmZsUDMx9wSdNKsCk+vSvE8yBjnCmUl7laSFj3LzPVrlSwgNYCF3kYnNAkah7NplK4SFhJYLwS0HCVCQJKa8rVbQLf9cBTu60U402mrgy0bN8xWoyimzbYbrOMJjalqkUg==",
'SigningCertUrl' => "https://sns.us-east-2.amazonaws.com/SimpleNotificationService-7ff5318490ec183fbaddaa2a969abfda.pem",
'UnsubscribeUrl' => "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:295079676684:publish-and-verify-62674b1d-4295-426b-88e7-5fb75652a04e:ad7d16e3-0a7c-46aa-b23e-ffaf02250cbe",
]
],
[
[
'Type' => "Notification",
'MessageId' => "11405cc3-9ac7-56d5-b45d-079e8f7a8edf",
'TopicArn' => "arn:aws:sns:us-east-2:295079676684:publish-and-verify-6e11fed2-fcdf-4c52-9dc4-36ef43f37f84",
'Subject' => "Hello world",
'Message' => "Hello world",
'Timestamp' => "2022-07-28T22:53:49.654Z",
'SignatureVersion' => "1",
'Signature' => "AItkS26d8yvnIKJevdirIPW7eM/yKbZy3/CF2EreCHmXWB3etWaV5Fb7SYpGABMpugpDZzNyGY1wCVWaopDoQ+7Q/kI2TpDu8bw1eExbi8U3kduvc/2m2fIrI4gDEY8/v3nzoLcr8pPodqMzrX6SzQou4klfaqbNK+rFmH0LVf2Q1VyOROODoSXmo4jg2Yu12jfxccBl96Drr/ihq4MJ4OcrWh6UzXXlVYjJHx2Ui4anNwNEb+Z4C2CAF1DjQUbhDtaoajDBPY+4d9C1OwbqwQpXsd6tyVcI9nFyEsVK8lfnAV+/3GZQcdXHbIUYBRGcBa4X5TlWJku5nDH2ERtHHw==",
'SigningCertUrl' => "https://sns.us-east-2.amazonaws.com/SimpleNotificationService-7ff5318490ec183fbaddaa2a969abfda.pem",
'UnsubscribeUrl' => "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:295079676684:publish-and-verify-6e11fed2-fcdf-4c52-9dc4-36ef43f37f84:adb318c3-2234-4c56-905d-c324cf0df874",
]
],
[
[
'Type' => "Notification",
'MessageId' => "4504e649-d933-5aa9-8199-bd14ccf05f0b",
'TopicArn' => "arn:aws:sns:us-east-2:295079676684:publish-and-verify-530b26da-0687-4fe4-9f71-780bad3181e2",
'Subject' => "Hello world",
'Message' => "Hello world",
'Timestamp' => "2022-07-28T22:53:55.086Z",
'SignatureVersion' => "2",
'Signature' => "cETcSvmmkt+My05qCLKexyl0+RyG83mSryKPqTfS+tYcxDJWVcjPJAr+qdpElzVaBl1aTGYVWMY64i9JqZ/JES8pylNj8LGvdhuNQKO59/WCoIimZAsNhn0xEgOeeDU+W/0BU4sdpCGMNjo0S/FuIiWaRe4E0YWRVrxeQevaQ70euDdfWgd5v1eCKQz8b367b9XBmMztL/CWUFI6YaKK/MV21eyvJe3Y7CtVYiOKEYiAZnAEkynK7gUGO5TsgDjGNYhj6U3xYsWgI03bmioSl7kdFSUj+AZ7ugas5fghqxgoDsdfqsjMYKRm5KKHQWsgzI619yIzpNKUiSMHxdZXpQ==",
'SigningCertUrl' => "https://sns.us-east-2.amazonaws.com/SimpleNotificationService-7ff5318490ec183fbaddaa2a969abfda.pem",
'UnsubscribeUrl' => "https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:295079676684:publish-and-verify-530b26da-0687-4fe4-9f71-780bad3181e2:db0ad2ad-03d1-48ca-a5da-51f317800a57"
]
]
];
}
private function getMockCertServerClient()
{
return function () {
return self::$certificate;
};
}
/**
* @dataProvider getHttpFixtures
*
* @param array $messageData
*/
public function testValidatesHttpFixtures($messageData)
{
$validator = new MessageValidator($this->getMockCertServerClient());
$message = new Message($messageData);
$this->assertTrue($validator->isValid($message));
$this->assertNotEmpty($message['SigningCertURL']);
}
/**
* @dataProvider getLambdaFixtures
*
* @param array $messageData
*/
public function testValidatesLambdaFixtures($messageData)
{
$validator = new MessageValidator($this->getMockCertServerClient());
$message = new Message($messageData);
$this->assertTrue($validator->isValid($message));
$this->assertNotEmpty($message['SigningCertUrl']);
}
}

View File

@ -0,0 +1,183 @@
<?php
namespace Aws\Sns;
use GuzzleHttp\Psr7\Request;
/**
* @covers \Aws\Sns\Message
*/
class MessageTest extends \PHPUnit_Framework_TestCase
{
public $messageData = array(
'Message' => 'a',
'MessageId' => 'b',
'Timestamp' => 'c',
'TopicArn' => 'd',
'Type' => 'e',
'Subject' => 'f',
'Signature' => 'g',
'SignatureVersion' => '1',
'SigningCertURL' => 'h',
'SubscribeURL' => 'i',
'Token' => 'j',
);
public function testGetters()
{
$message = new Message($this->messageData);
$this->assertInternalType('array', $message->toArray());
foreach ($this->messageData as $key => $expectedValue) {
$this->assertTrue(isset($message[$key]));
$this->assertEquals($expectedValue, $message[$key]);
}
}
public function testIterable()
{
$message = new Message($this->messageData);
$this->assertInstanceOf('Traversable', $message);
foreach ($message as $key => $value) {
$this->assertTrue(isset($this->messageData[$key]));
$this->assertEquals($value, $this->messageData[$key]);
}
}
/**
* @dataProvider messageTypeProvider
*
* @param string $messageType
*/
public function testConstructorSucceedsWithGoodData($messageType)
{
$this->assertInstanceOf('Aws\Sns\Message', new Message(
['Type' => $messageType] + $this->messageData
));
}
public function messageTypeProvider()
{
return [
['Notification'],
['SubscriptionConfirmation'],
['UnsubscribeConfirmation'],
];
}
/**
* @expectedException \InvalidArgumentException
*/
public function testConstructorFailsWithNoType()
{
$data = $this->messageData;
unset($data['Type']);
new Message($data);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testConstructorFailsWithMissingData()
{
new Message(['Type' => 'Notification']);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testRequiresTokenAndSubscribeUrlForSubscribeMessage()
{
new Message(
['Type' => 'SubscriptionConfirmation'] + array_diff_key(
$this->messageData,
array_flip(['Token', 'SubscribeURL'])
)
);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testRequiresTokenAndSubscribeUrlForUnsubscribeMessage()
{
new Message(
['Type' => 'UnsubscribeConfirmation'] + array_diff_key(
$this->messageData,
array_flip(['Token', 'SubscribeURL'])
)
);
}
public function testCanCreateFromRawPost()
{
$_SERVER['HTTP_X_AMZ_SNS_MESSAGE_TYPE'] = 'Notification';
// Prep php://input with mocked data
MockPhpStream::setStartingData(json_encode($this->messageData));
stream_wrapper_unregister('php');
stream_wrapper_register('php', __NAMESPACE__ . '\MockPhpStream');
$message = Message::fromRawPostData();
$this->assertInstanceOf('Aws\Sns\Message', $message);
stream_wrapper_restore("php");
unset($_SERVER['HTTP_X_AMZ_SNS_MESSAGE_TYPE']);
}
/**
* @expectedException \RuntimeException
*/
public function testCreateFromRawPostFailsWithMissingHeader()
{
Message::fromRawPostData();
}
/**
* @expectedException \RuntimeException
*/
public function testCreateFromRawPostFailsWithMissingData()
{
$_SERVER['HTTP_X_AMZ_SNS_MESSAGE_TYPE'] = 'Notification';
Message::fromRawPostData();
unset($_SERVER['HTTP_X_AMZ_SNS_MESSAGE_TYPE']);
}
public function testCanCreateFromPsr7Request()
{
$request = new Request(
'POST',
'/',
[],
json_encode($this->messageData)
);
$message = Message::fromPsrRequest($request);
$this->assertInstanceOf('Aws\Sns\Message', $message);
}
/**
* @expectedException \RuntimeException
*/
public function testCreateFromPsr7RequestFailsWithMissingData()
{
$request = new Request(
'POST',
'/',
[],
'Not valid JSON'
);
Message::fromPsrRequest($request);
}
public function testArrayAccess()
{
$message = new Message($this->messageData);
$this->assertInstanceOf('ArrayAccess', $message);
$message['foo'] = 'bar';
$this->assertTrue(isset($message['foo']));
$this->assertTrue($message['foo'] === 'bar');
unset($message['foo']);
$this->assertFalse(isset($message['foo']));
}
}

View File

@ -0,0 +1,242 @@
<?php
namespace Aws\Sns;
/**
* @covers Aws\Sns\MessageValidator
*/
class MessageValidatorTest extends \PHPUnit_Framework_TestCase
{
const VALID_CERT_URL = 'https://sns.foo.amazonaws.com/bar.pem';
private static $pKey;
private static $certificate;
public static function setUpBeforeClass()
{
self::$pKey = openssl_pkey_new();
$csr = openssl_csr_new([], self::$pKey);
$x509 = openssl_csr_sign($csr, null, self::$pKey, 1);
openssl_x509_export($x509, self::$certificate);
openssl_x509_free($x509);
}
public static function tearDownAfterClass()
{
openssl_pkey_free(self::$pKey);
}
public function testIsValidReturnsFalseOnFailedValidation()
{
$validator = new MessageValidator($this->getMockHttpClient());
$message = $this->getTestMessage([
'SignatureVersion' => '2',
]);
$this->assertFalse($validator->isValid($message));
}
/**
* @expectedException \Aws\Sns\Exception\InvalidSnsMessageException
* @expectedExceptionMessage The SignatureVersion "3" is not supported.
*/
public function testValidateFailsWhenSignatureVersionIsInvalid()
{
$validator = new MessageValidator($this->getMockCertServerClient());
$message = $this->getTestMessage([
'SignatureVersion' => '3',
]);
$validator->validate($message);
}
/**
* @expectedException \Aws\Sns\Exception\InvalidSnsMessageException
* @expectedExceptionMessage The certificate is located on an invalid domain.
*/
public function testValidateFailsWhenCertUrlInvalid()
{
$validator = new MessageValidator();
$message = $this->getTestMessage([
'SigningCertURL' => 'https://foo.amazonaws.com/bar.pem',
]);
$validator->validate($message);
}
/**
* @expectedException \Aws\Sns\Exception\InvalidSnsMessageException
* @expectedExceptionMessage The certificate is located on an invalid domain.
*/
public function testValidateFailsWhenCertUrlNotAPemFile()
{
$validator = new MessageValidator();
$message = $this->getTestMessage([
'SigningCertURL' => 'https://foo.amazonaws.com/bar',
]);
$validator->validate($message);
}
public function testValidatesAgainstCustomDomains()
{
$validator = new MessageValidator(
function () {
return self::$certificate;
},
'/^(foo|bar).example.com$/'
);
$message = $this->getTestMessage([
'SigningCertURL' => 'https://foo.example.com/baz.pem',
]);
$message['Signature'] = $this->getSignature($validator->getStringToSign($message));
$this->assertTrue($validator->isValid($message));
}
/**
* @expectedException \Aws\Sns\Exception\InvalidSnsMessageException
* @expectedExceptionMessageRegExp /Cannot get the certificate from ".+"./
*/
public function testValidateFailsWhenCannotGetCertificate()
{
$validator = new MessageValidator($this->getMockHttpClient(false));
$message = $this->getTestMessage();
$validator->validate($message);
}
/**
* @expectedException \Aws\Sns\Exception\InvalidSnsMessageException
* @expectedExceptionMessage Cannot get the public key from the certificate.
*/
public function testValidateFailsWhenCannotDeterminePublicKey()
{
$validator = new MessageValidator($this->getMockHttpClient());
$message = $this->getTestMessage();
$validator->validate($message);
}
/**
* @expectedException \Aws\Sns\Exception\InvalidSnsMessageException
* @expectedExceptionMessage The message signature is invalid.
*/
public function testValidateFailsWhenMessageIsInvalid()
{
$validator = new MessageValidator($this->getMockCertServerClient());
$message = $this->getTestMessage([
'Signature' => $this->getSignature('foo'),
]);
$validator->validate($message);
}
/**
* @expectedException \Aws\Sns\Exception\InvalidSnsMessageException
* @expectedExceptionMessage The message signature is invalid.
*/
public function testValidateFailsWhenSha256MessageIsInvalid()
{
$validator = new MessageValidator($this->getMockCertServerClient());
$message = $this->getTestMessage([
'Signature' => $this->getSignature('foo'),
'SignatureVersion' => '2'
]);
$validator->validate($message);
}
public function testValidateSucceedsWhenMessageIsValid()
{
$validator = new MessageValidator($this->getMockCertServerClient());
$message = $this->getTestMessage();
// Get the signature for a real message
$message['Signature'] = $this->getSignature($validator->getStringToSign($message));
// The message should validate
$this->assertTrue($validator->isValid($message));
}
public function testValidateSucceedsWhenSha256MessageIsValid()
{
$validator = new MessageValidator($this->getMockCertServerClient());
$message = $this->getTestMessage([
'SignatureVersion' => '2'
]);
// Get the signature for a real message
$message['Signature'] = $this->getSignature($validator->getStringToSign($message), '2');
// The message should validate
$this->assertTrue($validator->isValid($message));
}
public function testBuildsStringToSignCorrectly()
{
$validator = new MessageValidator();
$stringToSign = <<< STRINGTOSIGN
Message
foo
MessageId
bar
Timestamp
1435697129
TopicArn
baz
Type
Notification
STRINGTOSIGN;
$this->assertEquals(
$stringToSign,
$validator->getStringToSign($this->getTestMessage())
);
}
/**
* @param array $customData
*
* @return Message
*/
private function getTestMessage(array $customData = [])
{
return new Message($customData + [
'Message' => 'foo',
'MessageId' => 'bar',
'Timestamp' => time(),
'TopicArn' => 'baz',
'Type' => 'Notification',
'SigningCertURL' => self::VALID_CERT_URL,
'Signature' => true,
'SignatureVersion' => '1',
]);
}
private function getMockHttpClient($responseBody = '')
{
return function () use ($responseBody) {
return $responseBody;
};
}
private function getMockCertServerClient()
{
return function ($url) {
if ($url !== self::VALID_CERT_URL) {
return '';
}
return self::$certificate;
};
}
private function getSignature($stringToSign, $algo = '1')
{
if ($algo === '2') {
openssl_sign($stringToSign, $signature, self::$pKey, 'SHA256');
} else {
openssl_sign($stringToSign, $signature, self::$pKey);
}
return base64_encode($signature);
}
}
function time()
{
return 1435697129;
}

View File

@ -0,0 +1,60 @@
<?php
namespace Aws\Sns;
class MockPhpStream
{
private static $startingData = '';
private $index;
private $length;
private $data;
public static function setStartingData($data)
{
self::$startingData = $data;
}
public function __construct()
{
$this->data = self::$startingData;
$this->index = 0;
$this->length = strlen(self::$startingData);
}
public function stream_open($path, $mode, $options, &$opened_path)
{
return true;
}
public function stream_close()
{
}
public function stream_stat()
{
return array();
}
public function stream_flush()
{
return true;
}
public function stream_read($count)
{
$length = min($count, $this->length - $this->index);
$data = substr($this->data, $this->index);
$this->index = $this->index + $length;
return $data;
}
public function stream_eof()
{
return ($this->index >= $this->length);
}
public function stream_write($data)
{
return 0;
}
}