├── .github
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── NOTICE
├── README.md
├── build.xml
├── composer.json
├── phar-stub.php
├── phpunit-phar.xml
├── phpunit.xml
├── src
└── Amazon
│ └── InstantAccess
│ ├── Controllers
│ ├── AccountLinkingController.php
│ ├── Controller.php
│ └── PurchaseController.php
│ ├── Log
│ └── Logger.php
│ ├── Serialization
│ ├── Enums
│ │ ├── Enum.php
│ │ ├── FulfillPurchaseReasonValue.php
│ │ ├── FulfillPurchaseResponseValue.php
│ │ ├── GetUserIdResponseValue.php
│ │ ├── InstantAccessOperationValue.php
│ │ ├── RevokePurchaseReasonValue.php
│ │ ├── RevokePurchaseResponseValue.php
│ │ ├── SubscriptionActivateResponseValue.php
│ │ ├── SubscriptionDeactivatePeriodValue.php
│ │ ├── SubscriptionDeactivateReasonValue.php
│ │ └── SubscriptionDeactivateResponseValue.php
│ ├── FulfillPurchaseRequest.php
│ ├── FulfillPurchaseResponse.php
│ ├── GetUserIdRequest.php
│ ├── GetUserIdResponse.php
│ ├── InstantAccessRequest.php
│ ├── InstantAccessResponse.php
│ ├── RevokePurchaseRequest.php
│ ├── RevokePurchaseResponse.php
│ ├── SubscriptionActivateRequest.php
│ ├── SubscriptionActivateResponse.php
│ ├── SubscriptionDeactivateRequest.php
│ └── SubscriptionDeactivateResponse.php
│ ├── Signature
│ ├── AuthorizationHeader.php
│ ├── Credential.php
│ ├── CredentialStore.php
│ ├── Request.php
│ └── Signer.php
│ └── Utils
│ ├── DateUtils.php
│ ├── HttpUtils.php
│ └── IOUtils.php
├── tools
└── RequestBuilder.php
└── tst
├── Amazon
└── InstantAccess
│ ├── Controllers
│ ├── AccountLinkingControllerTest.php
│ └── PurchaseControllerTest.php
│ ├── Serialization
│ ├── Enums
│ │ └── InstantAccessOperationValueTest.php
│ ├── FulfillPurchaseRequestTest.php
│ ├── FulfillPurchaseResponseTest.php
│ ├── GetUserIdRequestTest.php
│ ├── GetUserIdResponseTest.php
│ ├── InstantAccessRequestTest.php
│ ├── InstantAccessResponseTest.php
│ ├── RevokePurchaseRequestTest.php
│ ├── RevokePurchaseResponseTest.php
│ ├── SubscriptionActivateRequestTest.php
│ ├── SubscriptionActivateResponseTest.php
│ ├── SubscriptionDeactivateRequestTest.php
│ └── SubscriptionDeactivateResponseTest.php
│ ├── Signature
│ ├── AuthorizationHeaderTest.php
│ ├── CredentialStoreTest.php
│ ├── CredentialTest.php
│ ├── RequestTest.php
│ └── SignerTest.php
│ └── Utils
│ ├── HttpUtilsTest.php
│ └── IOUtilsTest.php
├── bootstrap-phar.php
└── bootstrap.php
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | *Issue #, if available:*
2 |
3 | *Description of changes:*
4 |
5 |
6 | By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build
2 | vendor
3 | composer.lock
4 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
4 | documentation, we greatly value feedback and contributions from our community.
5 |
6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
7 | information to effectively respond to your bug report or contribution.
8 |
9 |
10 | ## Reporting Bugs/Feature Requests
11 |
12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features.
13 |
14 | When filing an issue, please check [existing open](https://github.com/amzn/amazon-instant-access-sdk-php/issues), or [recently closed](https://github.com/amzn/amazon-instant-access-sdk-php/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already
15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
16 |
17 | * A reproducible test case or series of steps
18 | * The version of our code being used
19 | * Any modifications you've made relevant to the bug
20 | * Anything unusual about your environment or deployment
21 |
22 |
23 | ## Contributing via Pull Requests
24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
25 |
26 | 1. You are working against the latest source on the *master* branch.
27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
29 |
30 | To send us a pull request, please:
31 |
32 | 1. Fork the repository.
33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
34 | 3. Ensure local tests pass.
35 | 4. Commit to your fork using clear commit messages.
36 | 5. Send us a pull request, answering any default questions in the pull request interface.
37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
38 |
39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
41 |
42 |
43 | ## Finding contributions to work on
44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/amzn/amazon-instant-access-sdk-php/labels/help%20wanted) issues is a great place to start.
45 |
46 |
47 | ## Code of Conduct
48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
50 | opensource-codeofconduct@amazon.com with any additional questions or comments.
51 |
52 |
53 | ## Security issue notifications
54 | 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.
55 |
56 |
57 | ## Licensing
58 |
59 | See the [LICENSE](https://github.com/amzn/amazon-instant-access-sdk-php/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
60 |
61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes.
62 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | include $(shell brazil-makefiles)
2 |
3 | build: clean
4 | @echo "build"
5 |
6 | clean:
7 | @echo "clean"
8 |
9 | test:
10 | @echo "test"
11 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Amazon Instance Access SDK for PHP
2 | Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Amazon Instant Access SDK for PHP
2 |
3 | Amazon Instant Access (AIA), is a fulfillment technology for virtual content that is purchased on the Amazon web site and needs to be
4 | delivered to a third party server. The **Amazon Instant Access SDK for PHP** enables PHP developers to easily integrate their systems and
5 | onboard with the AIA system.
6 |
--------------------------------------------------------------------------------
/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
33 | * {@link PurchaseController::onRevokePurchase()}
34 | * {@link PurchaseController::onSubscriptionActivate()}
35 | * {@link PurchaseController::onSubscriptionDeactivate()}
36 | *
37 | * The callbacks are called when the {@link Controller::process()} method is invoked.
38 | */
39 | class PurchaseController extends Controller
40 | {
41 | /** @var \Closure */
42 | private $fulfillPurchaseCallback;
43 | /** @var \Closure */
44 | private $revokePurchaseCallback;
45 | /** @var \Closure */
46 | private $subscriptionActivateCallback;
47 | /** @var \Closure */
48 | private $subscriptionDeactivateCallback;
49 |
50 | /**
51 | * Set the callback function for fulfill purchase
52 | *
53 | * @param \Closure $callback a callable object that receives a {@link FulfillPurchaseRequest} object
54 | * and returns a {@link FulfillPurchaseResponse}
55 | */
56 | public function onFulfillPurchase(\Closure $callback)
57 | {
58 | $this->fulfillPurchaseCallback = $callback;
59 | }
60 |
61 | /**
62 | * Set the callback function for revoke purchase
63 | *
64 | * @param \Closure $callback a callable object that receives a {@link RevokePurchaseRequest} object
65 | * and returns a {@link RevokePurchaseResponse}
66 | */
67 | public function onRevokePurchase(\Closure $callback)
68 | {
69 | $this->revokePurchaseCallback = $callback;
70 | }
71 |
72 | /**
73 | * Set the callback function for subscription activate
74 | *
75 | * @param \Closure $callback a callable object that receives a {@link SubscriptionActivateRequest} object
76 | * and returns a {@link SubscriptionActivateResponse}
77 | */
78 | public function onSubscriptionActivate(\Closure $callback)
79 | {
80 | $this->subscriptionActivateCallback = $callback;
81 | }
82 |
83 | /**
84 | * Set the callback function for subscription deactivate
85 | *
86 | * @param \Closure $callback a callable object that receives a {@link SubscriptionDeactivateRequest} object
87 | * and returns a {@link SubscriptionDeactivateResponse}
88 | */
89 | public function onSubscriptionDeactivate(\Closure $callback)
90 | {
91 | $this->subscriptionDeactivateCallback = $callback;
92 | }
93 |
94 | protected function processOperation($operation)
95 | {
96 | switch ($operation) {
97 | case InstantAccessOperationValue::PURCHASE:
98 | $callback = $this->fulfillPurchaseCallback;
99 | $request = FulfillPurchaseRequest::createFromJson($this->request->getBody());
100 | break;
101 | case InstantAccessOperationValue::REVOKE:
102 | $callback = $this->revokePurchaseCallback;
103 | $request = RevokePurchaseRequest::createFromJson($this->request->getBody());
104 | break;
105 | case InstantAccessOperationValue::SUBSCRIPTION_ACTIVATE:
106 | $callback = $this->subscriptionActivateCallback;
107 | $request = SubscriptionActivateRequest::createFromJson($this->request->getBody());
108 | break;
109 | case InstantAccessOperationValue::SUBSCRIPTION_DEACTIVATE:
110 | $callback = $this->subscriptionDeactivateCallback;
111 | $request = SubscriptionDeactivateRequest::createFromJson($this->request->getBody());
112 | break;
113 | default:
114 | throw new \InvalidArgumentException(
115 | sprintf('Operation %s is not supported by %s', $operation, get_class($this))
116 | );
117 | }
118 |
119 | if (!$callback) {
120 | throw new \UnexpectedValueException(sprintf('Callback not set for %s', $operation));
121 | }
122 |
123 | $iaResponse = $callback($request);
124 |
125 | return $iaResponse;
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/src/Amazon/InstantAccess/Log/Logger.php:
--------------------------------------------------------------------------------
1 | getConstants();
27 | }
28 |
29 | /**
30 | * Check if $name is a value supported by the enum
31 | *
32 | * @param string $name a string
33 | * @return boolean true if $name is a valid value in this enum, false otherwise
34 | */
35 | public static function isValid($name)
36 | {
37 | return in_array($name, self::getConstants());
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Amazon/InstantAccess/Serialization/Enums/FulfillPurchaseReasonValue.php:
--------------------------------------------------------------------------------
1 | setPurchaseToken($jsonObject->purchaseToken);
48 | $newObject->setUserId($jsonObject->userId);
49 | $newObject->setProductId($jsonObject->productId);
50 | $newObject->setReason($jsonObject->reason);
51 | };
52 |
53 | $object = parent::createFromJson($jsonString, $callback);
54 |
55 | return $object;
56 | }
57 |
58 | public function getPurchaseToken()
59 | {
60 | return $this->purchaseToken;
61 | }
62 |
63 | public function setPurchaseToken($purchaseToken)
64 | {
65 | $this->purchaseToken = $purchaseToken;
66 | return $this;
67 | }
68 |
69 | public function getUserId()
70 | {
71 | return $this->userId;
72 | }
73 |
74 | public function setUserId($userId)
75 | {
76 | $this->userId = $userId;
77 | return $this;
78 | }
79 |
80 | public function getProductId()
81 | {
82 | return $this->productId;
83 | }
84 |
85 | public function setProductId($productId)
86 | {
87 | $this->productId = $productId;
88 | return $this;
89 | }
90 |
91 | public function getReason()
92 | {
93 | return $this->reason;
94 | }
95 |
96 | /**
97 | * Set the request reason.
98 | *
99 | * @param string a string representation of the reason
100 | *
101 | * @see Amazon\InstantAccess\Serialization\Enums\FulfillPurchaseReasonValue For reason values
102 | */
103 | public function setReason($reason)
104 | {
105 | if (!FulfillPurchaseReasonValue::isValid($reason)) {
106 | throw new \InvalidArgumentException(sprintf('Invalid reason value: %s', $reason));
107 | }
108 |
109 | $this->reason = $reason;
110 | return $this;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/Amazon/InstantAccess/Serialization/FulfillPurchaseResponse.php:
--------------------------------------------------------------------------------
1 | response = $response;
41 | return $this;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Amazon/InstantAccess/Serialization/GetUserIdRequest.php:
--------------------------------------------------------------------------------
1 | setInfoField1($jsonObject->infoField1);
42 |
43 | // optional field
44 | if (isset($jsonObject->infoField2)) {
45 | $newObject->setInfoField2($jsonObject->infoField2);
46 | }
47 |
48 | // optional field
49 | if (isset($jsonObject->infoField3)) {
50 | $newObject->setInfoField3($jsonObject->infoField3);
51 | }
52 | };
53 |
54 | $object = parent::createFromJson($jsonString, $callback);
55 |
56 | return $object;
57 | }
58 |
59 | public function getInfoField1()
60 | {
61 | return $this->infoField1;
62 | }
63 |
64 | public function setInfoField1($infoField1)
65 | {
66 | $this->infoField1 = $infoField1;
67 | return $this;
68 | }
69 |
70 | public function getInfoField2()
71 | {
72 | return $this->infoField2;
73 | }
74 |
75 | public function setInfoField2($infoField2)
76 | {
77 | $this->infoField2 = $infoField2;
78 | return $this;
79 | }
80 |
81 | public function getInfoField3()
82 | {
83 | return $this->infoField3;
84 | }
85 |
86 | public function setInfoField3($infoField3)
87 | {
88 | $this->infoField3 = $infoField3;
89 | return $this;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/Amazon/InstantAccess/Serialization/GetUserIdResponse.php:
--------------------------------------------------------------------------------
1 | response = $response;
44 | return $this;
45 | }
46 |
47 | public function getUserId()
48 | {
49 | return $this->userId;
50 | }
51 |
52 | public function setUserId($userId)
53 | {
54 | $this->userId = $userId;
55 | return $this;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Amazon/InstantAccess/Serialization/InstantAccessRequest.php:
--------------------------------------------------------------------------------
1 | setOperation($jsonObject->operation);
55 | // and the specific fields
56 | if ($callback && is_callable($callback)) {
57 | $callback($newObject, $jsonObject);
58 | }
59 | } catch (\Exception $e) {
60 | throw new \InvalidArgumentException(
61 | sprintf('Unable to deserialized object: %s. %s', $type, $e->getMessage())
62 | );
63 | }
64 |
65 | return $newObject;
66 | }
67 |
68 | public function getOperation()
69 | {
70 | return $this->operation;
71 | }
72 |
73 | /**
74 | * Set the request operation
75 | *
76 | * @param string a string representation of the operation
77 | *
78 | * @see Amazon\InstantAccess\Serialization\Enums\InstantAccessOperationValue For operation values
79 | */
80 | public function setOperation($operation)
81 | {
82 | if (!InstantAccessOperationValue::isValid($operation)) {
83 | throw new \InvalidArgumentException(sprintf('Invalid operation value: %s', $reason));
84 | }
85 |
86 | $this->operation = $operation;
87 | return $this;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/Amazon/InstantAccess/Serialization/InstantAccessResponse.php:
--------------------------------------------------------------------------------
1 | response;
40 | }
41 |
42 | public function setResponse($response)
43 | {
44 | $this->response = $response;
45 | return $this;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Amazon/InstantAccess/Serialization/RevokePurchaseRequest.php:
--------------------------------------------------------------------------------
1 | setPurchaseToken($jsonObject->purchaseToken);
48 | $newObject->setUserId($jsonObject->userId);
49 | $newObject->setProductId($jsonObject->productId);
50 | $newObject->setReason($jsonObject->reason);
51 | };
52 |
53 | $object = parent::createFromJson($jsonString, $callback);
54 |
55 | return $object;
56 | }
57 |
58 | public function getPurchaseToken()
59 | {
60 | return $this->purchaseToken;
61 | }
62 |
63 | public function setPurchaseToken($purchaseToken)
64 | {
65 | $this->purchaseToken = $purchaseToken;
66 | return $this;
67 | }
68 |
69 | public function getUserId()
70 | {
71 | return $this->userId;
72 | }
73 |
74 | public function setUserId($userId)
75 | {
76 | $this->userId = $userId;
77 | return $this;
78 | }
79 |
80 | public function getProductId()
81 | {
82 | return $this->productId;
83 | }
84 |
85 | public function setProductId($productId)
86 | {
87 | $this->productId = $productId;
88 | return $this;
89 | }
90 |
91 | public function getReason()
92 | {
93 | return $this->reason;
94 | }
95 |
96 | /**
97 | * Set the request reason
98 | *
99 | * @param string a string representation of the reason
100 | *
101 | * @see Amazon\InstantAccess\Serialization\Enums\RevokePurchaseReasonValue For reason values
102 | */
103 | public function setReason($reason)
104 | {
105 | if (!RevokePurchaseReasonValue::isValid($reason)) {
106 | throw new \InvalidArgumentException(sprintf('Invalid reason value: %s', $reason));
107 | }
108 |
109 | $this->reason = $reason;
110 | return $this;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/Amazon/InstantAccess/Serialization/RevokePurchaseResponse.php:
--------------------------------------------------------------------------------
1 | response = $response;
41 | return $this;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Amazon/InstantAccess/Serialization/SubscriptionActivateRequest.php:
--------------------------------------------------------------------------------
1 | setSubscriptionId($jsonObject->subscriptionId);
46 | $newObject->setProductId($jsonObject->productId);
47 | $newObject->setUserId($jsonObject->userId);
48 | if (isset($jsonObject->subscriptionGroupId)) {
49 | $newObject->setSubscriptionGroupId($jsonObject->subscriptionGroupId);
50 | }
51 | if (isset($jsonObject->numberOfSubscriptionsInGroup)) {
52 | $newObject->setNumberOfSubscriptionsInGroup($jsonObject->numberOfSubscriptionsInGroup);
53 | }
54 | };
55 |
56 | $object = parent::createFromJson($jsonString, $callback);
57 |
58 | return $object;
59 | }
60 |
61 | public function getSubscriptionId()
62 | {
63 | return $this->subscriptionId;
64 | }
65 |
66 | public function setSubscriptionId($subscriptionId)
67 | {
68 | $this->subscriptionId = $subscriptionId;
69 | return $this;
70 | }
71 |
72 | public function getProductId()
73 | {
74 | return $this->productId;
75 | }
76 |
77 | public function setProductId($productId)
78 | {
79 | $this->productId = $productId;
80 | return $this;
81 | }
82 |
83 | public function getUserId()
84 | {
85 | return $this->userId;
86 | }
87 |
88 | public function setUserId($userId)
89 | {
90 | $this->userId = $userId;
91 | return $this;
92 | }
93 |
94 | public function getSubscriptionGroupId()
95 | {
96 | return $this->subscriptionGroupId;
97 | }
98 |
99 | public function setSubscriptionGroupId($subscriptionGroupId)
100 | {
101 | $this->subscriptionGroupId = $subscriptionGroupId;
102 | return $this;
103 | }
104 |
105 | public function getNumberOfSubscriptionsInGroup()
106 | {
107 | return $this->numberOfSubscriptionsInGroup;
108 | }
109 |
110 | public function setNumberOfSubscriptionsInGroup($numberOfSubscriptionsInGroup)
111 | {
112 | $this->numberOfSubscriptionsInGroup = $numberOfSubscriptionsInGroup;
113 | return $this;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/Amazon/InstantAccess/Serialization/SubscriptionActivateResponse.php:
--------------------------------------------------------------------------------
1 | response = $response;
41 | return $this;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Amazon/InstantAccess/Serialization/SubscriptionDeactivateRequest.php:
--------------------------------------------------------------------------------
1 | setSubscriptionId($jsonObject->subscriptionId);
48 | $newObject->setReason($jsonObject->reason);
49 | $newObject->setPeriod($jsonObject->period);
50 | };
51 |
52 | $object = parent::createFromJson($jsonString, $callback);
53 |
54 | return $object;
55 | }
56 |
57 | public function getSubscriptionId()
58 | {
59 | return $this->subscriptionId;
60 | }
61 |
62 | public function setSubscriptionId($subscriptionId)
63 | {
64 | $this->subscriptionId = $subscriptionId;
65 | return $this;
66 | }
67 |
68 | public function getReason()
69 | {
70 | return $this->reason;
71 | }
72 |
73 | /**
74 | * Set the request reason
75 | *
76 | * @param string a string representation of the reason
77 | *
78 | * @see Amazon\InstantAccess\Serialization\Enums\SubscriptionDeactivateReasonValue For reason values
79 | */
80 | public function setReason($reason)
81 | {
82 | if (!SubscriptionDeactivateReasonValue::isValid($reason)) {
83 | throw new \InvalidArgumentException(sprintf('Invalid reason value: %s', $reason));
84 | }
85 |
86 | $this->reason = $reason;
87 | return $this;
88 | }
89 |
90 | public function getPeriod()
91 | {
92 | return $this->period;
93 | }
94 |
95 | /**
96 | * Set the request period
97 | *
98 | * @param string a string representation of the period
99 | *
100 | * @see Amazon\InstantAccess\Serialization\Enums\SubscriptionDeactivatePeriodValue For period values
101 | */
102 | public function setPeriod($period)
103 | {
104 | if (!SubscriptionDeactivatePeriodValue::isValid($period)) {
105 | throw new \InvalidArgumentException(sprintf('Invalid period value: %s', $reason));
106 | }
107 |
108 | $this->period = $period;
109 | return $this;
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/Amazon/InstantAccess/Serialization/SubscriptionDeactivateResponse.php:
--------------------------------------------------------------------------------
1 | response = $response;
41 | return $this;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Amazon/InstantAccess/Signature/AuthorizationHeader.php:
--------------------------------------------------------------------------------
1 |
24 | * Authorization: ALGORITHM Credential=CREDENTIAL, SignedHeaders=SIGNED_HEADERS, Signature=SIGNATURE
25 | *
26 | *
27 | * Where:
28 | * ALGORITHM := The signing algorithm used for the credential, ex. DTAv1-SHA-256
29 | * CREDENTIAL := KEYID/DATE.
30 | * SIGNED_HEADERS := lower cased header names sorted by byte order joined with semicolons.
31 | * SIGNATURE := The signature calculated by the signing algorithm.
32 | * KEYID := The public id for the sceret key used to calculate the signature.
33 | * DATE := The date the message was signed in YYMMDD format. This is used to generate the daily key.
34 | */
35 | class AuthorizationHeader
36 | {
37 | /** @var string */
38 | private $algorithm;
39 | /** @var string */
40 | private $credential;
41 | /** @var string */
42 | private $signedHeaders;
43 | /** @var string */
44 | private $signature;
45 |
46 | const AUTHORIZATION_HEADER_PATTERN = '/(\S+) SignedHeaders=(\S+), Credential=(\S+), Signature=([\S]+)$/';
47 |
48 | /**
49 | * Parses header value string an returns a new object.
50 | *
51 | * @param string $headerString the string representation of the Authentication header
52 | *
53 | * @return AuthorizationHeader a new {@link AuthorizationHeader} object if header is valid
54 | *
55 | * @throws InvalidArgumentException if unable to parse header
56 | */
57 | public static function parse($headerString)
58 | {
59 | if (empty($headerString)) {
60 | throw new \InvalidArgumentException('Invalid authorization header.');
61 | }
62 |
63 | preg_match(self::AUTHORIZATION_HEADER_PATTERN, $headerString, $matches);
64 |
65 | if (!$matches) {
66 | throw new \InvalidArgumentException('Unable to parse authorization header.');
67 | }
68 |
69 | // split headers by ';' and convert them to lower case
70 | $signedHeaders = array_map('strtolower', explode(';', $matches[2]));
71 |
72 | // the credential should follow this pattern: PUBLIC_KEY_ID/DATE
73 | $credential = explode('/', $matches[3]);
74 |
75 | if (count($credential) < 2) {
76 | throw new \InvalidArgumentException('Invalid credential format.');
77 | }
78 |
79 | $credential = array('key' => $credential[0],
80 | 'date' => $credential[1]);
81 |
82 | // the algorithm used to generate the signature
83 | $algorithm = $matches[1];
84 |
85 | // the signature of the request
86 | $signature = $matches[4];
87 |
88 | $header = new AuthorizationHeader(
89 | $algorithm,
90 | $signedHeaders,
91 | $credential,
92 | $signature
93 | );
94 |
95 | return $header;
96 | }
97 |
98 | public function __construct($algorithm, $signedHeaders, $credential, $signature)
99 | {
100 | if (empty($algorithm)) {
101 | throw new \InvalidArgumentException('Empty algorithm information.');
102 | }
103 |
104 | if (!is_array($signedHeaders)) {
105 | throw new \InvalidArgumentException('Invalid signed headers array.');
106 | }
107 |
108 | if (!is_array($credential) || !array_key_exists('key', $credential) || !array_key_exists('date', $credential)) {
109 | throw new \InvalidArgumentException('Invalid credential information.');
110 | }
111 |
112 | if (empty($signature)) {
113 | throw new \InvalidArgumentException('Empty signature information.');
114 | }
115 |
116 | $this->algorithm = $algorithm;
117 | $this->signedHeaders = $signedHeaders;
118 | $this->credential = $credential;
119 | $this->signature = $signature;
120 | }
121 |
122 | public function getAlgorithm()
123 | {
124 | return $this->algorithm;
125 | }
126 |
127 | public function getCredential()
128 | {
129 | return $this->credential;
130 | }
131 |
132 | public function getSignedHeaders()
133 | {
134 | return $this->signedHeaders;
135 | }
136 |
137 | public function getSignature()
138 | {
139 | return $this->signature;
140 | }
141 |
142 | public function __tostring()
143 | {
144 | $str = $this->algorithm . ' ';
145 | $str .= 'SignedHeaders=' . implode(';', $this->signedHeaders) . ', ';
146 | $str .= 'Credential=' . $this->credential['key'] . '/'. $this->credential['date'] .', ';
147 | $str .= 'Signature=' . $this->signature;
148 |
149 | return $str;
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/src/Amazon/InstantAccess/Signature/Credential.php:
--------------------------------------------------------------------------------
1 | secretKey = $secretKey;
35 | $this->publicKey = $publicKey;
36 | }
37 |
38 | /**
39 | * Gets the secret key.
40 | *
41 | * @return string the secret key
42 | */
43 | public function getSecretKey()
44 | {
45 | return $this->secretKey;
46 | }
47 |
48 | /**
49 | * Gets the public key.
50 | *
51 | * @return string the public key
52 | */
53 | public function getPublicKey()
54 | {
55 | return $this->publicKey;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/Amazon/InstantAccess/Signature/CredentialStore.php:
--------------------------------------------------------------------------------
1 | store)) {
46 | return null;
47 | }
48 |
49 | return $this->store[$publicKey];
50 | }
51 |
52 | /**
53 | * Gets the credentials stored in this store.
54 | *
55 | * @return array an array with all the credentials
56 | */
57 | public function getAll()
58 | {
59 | return $this->store;
60 | }
61 |
62 | /**
63 | * Adds the new credential to the store. If the store already contains the public key, the credential is replaced.
64 | *
65 | * @param Credential credential the credential object to be added
66 | */
67 | public function add(Credential $credential)
68 | {
69 | $this->store[$credential->getPublicKey()] = $credential;
70 | }
71 |
72 | /**
73 | * Removes the credential from the store.
74 | *
75 | * @param string $publicKey the public key of the credential to be removed
76 | */
77 | public function remove($publicKey)
78 | {
79 | unset($this->store[$publicKey]);
80 | }
81 |
82 | /**
83 | * Loads keys from a file and populates the store.
84 | *
85 | * Each line of the file must contain a secret key and a public key separated by an empty space.
86 | *
87 | * @param string $filePath the path of the file that contains the keys
88 | *
89 | * @throws InvalidArgumentException if the file does not exist
90 | */
91 | public function loadFromFile($filePath)
92 | {
93 | if (empty($filePath) || !file_exists($filePath)) {
94 | $message = 'Invalid keys file path';
95 | throw new \InvalidArgumentException($message);
96 | }
97 |
98 | $contents = file_get_contents($filePath);
99 |
100 | $this->load($contents);
101 | }
102 |
103 | /**
104 | * Loads keys from a string and populates the store.
105 | *
106 | * Each line of the file must contain a secret key and a public key separated by an empty space.
107 | *
108 | * @param string $contents the string object that contains the keys
109 | *
110 | * @throws InvalidArgumentException if the content is empty or malformed
111 | */
112 | public function load($contents)
113 | {
114 | if (empty($contents)) {
115 | $message = 'Empty key container';
116 | throw new \InvalidArgumentException($message);
117 | }
118 |
119 | $lines = explode(PHP_EOL, $contents);
120 |
121 | foreach ($lines as $i => $line) {
122 | // Ignore blank lines in between credentials
123 | if (!$line) {
124 | continue;
125 | }
126 |
127 | // credentials should be separate by an empty space
128 | $keys = preg_split('/\s+/', $line);
129 |
130 | // Invalid format
131 | if (count($keys) < 2) {
132 | $message = 'Invalid credentials format found on line ' . $i;
133 | throw new \InvalidArgumentException($message);
134 | }
135 |
136 | $secretKey = $keys[0];
137 | $publicKey = $keys[1];
138 |
139 | $this->store[$publicKey] = new Credential($secretKey, $publicKey);
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/Amazon/InstantAccess/Signature/Request.php:
--------------------------------------------------------------------------------
1 | url = HttpUtils::parseFullURL($server);
49 | $this->method = $server['REQUEST_METHOD'];
50 | $this->headers = HttpUtils::parseRequestHeaders($server);
51 | $this->body = $requestBody;
52 | }
53 |
54 | public function getUrl()
55 | {
56 | return $this->url;
57 | }
58 |
59 | public function getMethod()
60 | {
61 | return $this->method;
62 | }
63 |
64 | public function getBody()
65 | {
66 | return $this->body;
67 | }
68 |
69 | public function &getHeaders()
70 | {
71 | return $this->headers;
72 | }
73 |
74 | /**
75 | * Remove headers that are present in $filter
76 | *
77 | * @param array $filter array of headers to be removed from this request
78 | */
79 | public function filterHeaders(array $filter)
80 | {
81 | $this->headers = array_diff_key($this->headers, array_flip($filter));
82 | }
83 |
84 | /**
85 | * @return string a human-friendly string representation of the Request
86 | */
87 | public function __tostring()
88 | {
89 | $headersStr = implode(
90 | ', ',
91 | array_map(
92 | function ($v, $k) {
93 | return sprintf("%s:'%s'", $k, $v);
94 | },
95 | $this->getHeaders(),
96 | array_keys($this->getHeaders())
97 | )
98 | );
99 |
100 | $bodyStr = trim(preg_replace('/\s+/', ' ', $this->getBody()));
101 |
102 | return sprintf(
103 | 'Method: %s, Url: %s, Headers: %s, Body: %s',
104 | $this->getMethod(),
105 | $this->getMethod(),
106 | $headersStr,
107 | $bodyStr
108 | );
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/Amazon/InstantAccess/Signature/Signer.php:
--------------------------------------------------------------------------------
1 | format(DateUtils::DATE_FORMAT_SHORT);
53 | $isoDate = $dateNow->format(DateUtils::DATE_FORMAT_ISO8601);
54 |
55 | $headers = &$request->getHeaders();
56 |
57 | $headers[HttpUtils::X_AMZ_DATE_HEADER] = $isoDate;
58 |
59 | // Remove the Authorization header from the request, since it could have been set if sign() was previously
60 | // called on this request.
61 | unset($headers[HttpUtils::AUTHORIZATION_HEADER]);
62 |
63 | $authorizationHeader = $this->getAuthorizationHeader($request, $credential, $shortDate, $isoDate);
64 |
65 | Logger::getLogger()->debug(sprintf('Signing request with header: %s', $authorizationHeader));
66 |
67 | $headers[HttpUtils::AUTHORIZATION_HEADER] = $authorizationHeader;
68 | }
69 |
70 | /**
71 | * Verifies the request signature against a credential store.
72 | *
73 | * @param Request $request the request to verify.
74 | * @param CredentialStore $credentialStore the credential store used to verify the request.
75 | *
76 | * @return boolean returns true if the request validates.
77 | */
78 | public function verify(Request $request, CredentialStore $credentialStore)
79 | {
80 | if (count($credentialStore->getAll()) == 0) {
81 | throw new \InvalidArgumentException('Empty credential store.');
82 | }
83 |
84 | $dateNow = new \DateTime('@' . time());
85 |
86 | $shortDate = $dateNow->format(DateUtils::DATE_FORMAT_SHORT);
87 |
88 | $headers = $request->getHeaders();
89 |
90 | if (!isset($headers[HttpUtils::X_AMZ_DATE_HEADER])) {
91 | Logger::getLogger()->warning(
92 | sprintf('Header %s not found, aborting request verification.', HttpUtils::X_AMZ_DATE_HEADER)
93 | );
94 | return false;
95 | }
96 | $requestIsoDate = $headers[HttpUtils::X_AMZ_DATE_HEADER];
97 |
98 | if (!isset($headers[HttpUtils::AUTHORIZATION_HEADER])) {
99 | Logger::getLogger()->warning(
100 | sprintf('Header %s not found, aborting request verification.', HttpUtils::AUTHORIZATION_HEADER)
101 | );
102 | return false;
103 | }
104 | $actualAuthorization = $headers[HttpUtils::AUTHORIZATION_HEADER];
105 |
106 | $authorizationHeader = null;
107 | try {
108 | $authorizationHeader = AuthorizationHeader::parse($actualAuthorization);
109 | } catch (\InvalidArgumentException $e) {
110 | Logger::getLogger()->warning(
111 | sprintf('Unable to parse Authorization header, aborting request verification.')
112 | );
113 | return false;
114 | }
115 |
116 | $signedHeaders = $authorizationHeader->getSignedHeaders();
117 |
118 | // remove unsigned headers
119 | $unsignedHeaders = array_diff_key(($request->getHeaders()), array_flip($signedHeaders));
120 | $request->filterHeaders(array_keys($unsignedHeaders));
121 |
122 | $dateOfRequest = \DateTime::createFromFormat(DateUtils::DATE_FORMAT_ISO8601, $requestIsoDate);
123 | $delta = $dateOfRequest->getTimestamp() - $dateNow->getTimestamp();
124 | if (abs($delta) > self::TIME_TOLERANCE_IN_MS) {
125 | Logger::getLogger()->warning(sprintf('Time tolerance exceeded, aborting request verification.'));
126 | return false;
127 | }
128 |
129 | $credentialInfo = $authorizationHeader->getCredential();
130 |
131 | $credential = $credentialStore->get($credentialInfo['key']);
132 | if (!$credential) {
133 | Logger::getLogger()->warning(
134 | sprintf('Public key not found: %s, aborting request verification.', $credentialInfo['key'])
135 | );
136 | return false;
137 | }
138 |
139 | if ($dateOfRequest->format(DateUtils::DATE_FORMAT_SHORT) != $credentialInfo['date']) {
140 | Logger::getLogger()->warning(
141 | sprintf('Request date and credential date don`t match aborting request verification.')
142 | );
143 | return false;
144 | }
145 |
146 | $authorizationHeader = $this->getAuthorizationHeader($request, $credential, $shortDate, $requestIsoDate);
147 |
148 | Logger::getLogger()->debug(
149 | sprintf(
150 | 'Verifying request with header: %s, against expected header: %s',
151 | $actualAuthorization,
152 | $authorizationHeader
153 | )
154 | );
155 |
156 | if ($authorizationHeader == $actualAuthorization) {
157 | return true;
158 | } else {
159 | Logger::getLogger()->warning(sprintf('Authorization signature doesn`t match, verification failed.'));
160 | return false;
161 | }
162 | }
163 |
164 | /**
165 | * Returns the autorization header based on the parameters
166 | *
167 | * @param Request $request the request to generate the signature from
168 | * @param Credential $credential the credential to use when signing
169 | * @param string $shortDate the date to use to sign in short format
170 | * @param string $isoDate the date to use to sign in iso format
171 | *
172 | * @return string a string representation of the authorization header
173 | */
174 | public function getAuthorizationHeader(Request $request, Credential $credential, $shortDate, $isoDate)
175 | {
176 | if (!$shortDate || !$isoDate) {
177 | throw new \InvalidArgumentException('Invalid dates.');
178 | }
179 |
180 | $timedKey = hash_hmac('sha256', $shortDate, $credential->getSecretKey(), true);
181 |
182 | $canonicalRequest = $this->getCanonicalRequest($request);
183 |
184 | // We don't use scope in this algorithm
185 | $scope = '';
186 |
187 | $stringToSign = self::ALGORITHM_ID . "\n" . $isoDate . "\n" . $scope . "\n" . hash('sha256', $canonicalRequest);
188 |
189 | Logger::getLogger()->debug(sprintf('String to sign: %s', $stringToSign));
190 |
191 | $signature = hash_hmac('sha256', $stringToSign, $timedKey);
192 |
193 | $headers = $request->getHeaders();
194 | ksort($headers);
195 |
196 | $authorizationHeader = new AuthorizationHeader(
197 | self::ALGORITHM_ID,
198 | array_keys($headers),
199 | array('key' => $credential->getPublicKey(), 'date' => $shortDate),
200 | $signature
201 | );
202 |
203 | return $authorizationHeader->__tostring();
204 | }
205 |
206 | /**
207 | * Returns the canonical representation of the request. The canonical request is of the form:
208 | *
209 | *
210 | * METHOD 211 | * CANONICAL_PATH 212 | * CANONICAL_QUERY_STRING 213 | * CANONICAL_HEADER_STRING 214 | * SIGNED_HEADERS 215 | * CONTENT_HASH 216 | *217 | * 218 | * Which for a get request to http://amazon.com/ would be: 219 | * 220 | *
221 | * GET 222 | * / 223 | * 224 | * x-amz-date:20110909T233600Z 225 | * 230d8358dc8e8890b4c58deeb62912ee2f20357ae92a5cc861b98e68fe31acb5 226 | *227 | * 228 | * @param Request $request the request to canonicalize. 229 | * @return string the canonical request. 230 | */ 231 | private function getCanonicalRequest(Request $request) 232 | { 233 | // Method 234 | $canonicalRequest = $request->getMethod() . "\n"; 235 | 236 | // Path 237 | $url = parse_url($request->getUrl()); 238 | $canonicalRequest .= HttpUtils::normalizePath($url['path']) . "\n"; 239 | 240 | // Query string 241 | // TODO: the Java SDK does not add the query string to the canonical form 242 | $canonicalRequest .= "\n"; 243 | 244 | // Headers 245 | $headers = array(); 246 | foreach ($request->getHeaders() as $key => $value) { 247 | $headers[$key] = preg_replace('/\s+/', ' ', trim($value)); 248 | } 249 | 250 | ksort($headers); 251 | 252 | foreach ($headers as $key => $value) { 253 | $canonicalRequest .= $key . ':' . $value . "\n"; 254 | } 255 | 256 | // TODO: the Java SDK adds a empty line after the headers 257 | $canonicalRequest .= "\n"; 258 | 259 | // Signed headers 260 | $headers = $request->getHeaders(); 261 | ksort($headers); 262 | 263 | $canonicalRequest .= implode(';', array_keys($headers)) . "\n"; 264 | 265 | // Content hash 266 | $canonicalRequest .= hash('sha256', $request->getBody()); 267 | 268 | Logger::getLogger()->debug(sprintf('Canonical request: %s', $canonicalRequest)); 269 | 270 | return $canonicalRequest; 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /src/Amazon/InstantAccess/Utils/DateUtils.php: -------------------------------------------------------------------------------- 1 | $value) { 98 | if (substr($key, 0, 5) == 'HTTP_') { 99 | $key = substr($key, 5); 100 | } elseif ($key != 'CONTENT_TYPE' && $key != 'CONTENT_LENGTH') { 101 | continue; 102 | } 103 | 104 | $header = str_replace('_', '-', strtolower($key)); 105 | $headers[$header] = $value; 106 | } 107 | 108 | return $headers; 109 | } 110 | 111 | /** 112 | * Normalize the URL according to RFC 3986. 113 | * 114 | * This function replaces multiple slashses with a single one and eliminates '.' (current directory) and 115 | * '..' (parent directory). 116 | * 117 | * @param string a path 118 | * 119 | * @return string a normalized path 120 | */ 121 | public static function normalizePath($path) 122 | { 123 | if (!$path) { 124 | return '/'; 125 | } 126 | 127 | $parts = explode('/', $path); 128 | 129 | $normalizedParts = array(); 130 | foreach ($parts as $part) { 131 | if ($part == '..') { 132 | array_pop($normalizedParts); 133 | } elseif ($part != '.' && $part != '') { 134 | $normalizedParts[] = $part; 135 | } 136 | } 137 | 138 | $newPath = ''; 139 | 140 | if ($path[0] == '/') { 141 | $newPath .= '/'; 142 | } 143 | 144 | $newPath .= implode('/', $normalizedParts); 145 | 146 | if ($path != '/' && end($parts) == '') { 147 | $newPath .= '/'; 148 | } 149 | 150 | return $newPath; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/Amazon/InstantAccess/Utils/IOUtils.php: -------------------------------------------------------------------------------- 1 | pushHandler(new Monolog\Handler\StreamHandler('php://stdout')); 16 | Logger::setLogger($monolog); 17 | 18 | $server = array(); 19 | //$server['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36'; 20 | $server['HTTP_HOST'] = 'localhost'; 21 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 22 | $server['SERVER_PORT'] = '8000'; 23 | $server['REQUEST_URI'] = '/service/linking'; 24 | $server['REQUEST_METHOD'] = 'POST'; 25 | $server['CONTENT_TYPE'] = 'application/json'; 26 | 27 | $content = '{ 28 | "operation": "GetUserId", 29 | "infoField1": "nobody@amazon.com", 30 | "infoField2": "nobody" 31 | }'; 32 | 33 | $content = trim(preg_replace('/\s+/', ' ', $content)); 34 | 35 | $request = new Request($server, $content); 36 | 37 | $credential = new Credential('e2c4905c-83ba-41e7-9c1b-af8014a334cb', '367caa91-cde5-48f2-91fe-bb95f546e9f0'); 38 | 39 | $signer = new Signer(); 40 | 41 | $signer->sign($request, $credential); 42 | 43 | //echo $request; 44 | 45 | $cmd = 'curl -v'; 46 | $cmd .= ' --data \'' . $request->getBody() . '\''; 47 | foreach ($request->getHeaders() as $key => $value) { 48 | $cmd .= ' -H "' . $key . ':' . $value . '"'; 49 | } 50 | $cmd .= ' ' . $request->getUrl(); 51 | 52 | echo $cmd; 53 | 54 | system($cmd); 55 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Controllers/AccountLinkingControllerTest.php: -------------------------------------------------------------------------------- 1 | add($credential); 42 | 43 | self::$credential = $credential; 44 | self::$credentialStore = $credentialStore; 45 | } 46 | 47 | private function generateSignedRequest(&$server, $content) 48 | { 49 | $dateNow = new \DateTime('@' . time()); 50 | 51 | $shortDate = $dateNow->format(DateUtils::DATE_FORMAT_SHORT); 52 | $isoDate = $dateNow->format(DateUtils::DATE_FORMAT_ISO8601); 53 | 54 | $server['HTTP_' . HttpUtils::X_AMZ_DATE_HEADER] = $isoDate; 55 | 56 | $request = new Request($server, $content); 57 | 58 | $signer = new Signer(); 59 | $signer->sign($request, self::$credential); 60 | 61 | $headers = $request->getHeaders(); 62 | $server['HTTP_' . HttpUtils::AUTHORIZATION_HEADER] = $headers[HttpUtils::AUTHORIZATION_HEADER]; 63 | 64 | return $request; 65 | } 66 | 67 | public function testGetUserId() 68 | { 69 | $server = array(); 70 | $server['HTTP_HOST'] = 'amazon.com'; 71 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 72 | $server['SERVER_PORT'] = '80'; 73 | $server['REQUEST_URI'] = '/'; 74 | $server['REQUEST_METHOD'] = 'POST'; 75 | $server['CONTENT_TYPE'] = 'application/json'; 76 | 77 | $content = '{ 78 | "operation": "GetUserId", 79 | "infoField1": "nobody@amazon.com", 80 | "infoField2": "amazon", 81 | "infoField3": "nobody" 82 | }'; 83 | 84 | $request = $this->generateSignedRequest($server, $content); 85 | 86 | $body = tmpfile(); 87 | fwrite($body, $content); 88 | fseek($body, 0); 89 | 90 | $controller = new AccountLinkingController(self::$credentialStore); 91 | 92 | $controller->onGetUserId(function ($req) { 93 | $res = new GetUserIdResponse(); 94 | $res->setResponse(GetUserIdResponseValue::OK); 95 | $res->setUserId('1234'); 96 | return $res; 97 | }); 98 | 99 | $response = $controller->process($server, IOUtils::getFilePathFromHandle($body)); 100 | 101 | $this->assertNotNull($response); 102 | $this->assertEquals('{"userId":"1234","response":"OK"}', $response); 103 | } 104 | 105 | public function testGetUserIdNoCallback() 106 | { 107 | $server = array(); 108 | $server['HTTP_HOST'] = 'amazon.com'; 109 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 110 | $server['SERVER_PORT'] = '80'; 111 | $server['REQUEST_URI'] = '/'; 112 | $server['REQUEST_METHOD'] = 'POST'; 113 | $server['CONTENT_TYPE'] = 'application/json'; 114 | 115 | $content = '{ 116 | "operation": "GetUserId", 117 | "infoField1": "nobody@amazon.com", 118 | "infoField2": "amazon", 119 | "infoField3": "nobody" 120 | }'; 121 | 122 | $request = $this->generateSignedRequest($server, $content); 123 | 124 | $body = tmpfile(); 125 | fwrite($body, $content); 126 | fseek($body, 0); 127 | 128 | $controller = new AccountLinkingController(self::$credentialStore); 129 | 130 | $response = $controller->process($server, IOUtils::getFilePathFromHandle($body)); 131 | 132 | $this->assertEmpty($response); 133 | 134 | // TODO : assert headers for status 500 135 | } 136 | 137 | public function testProcessOperationInvalidOperation() 138 | { 139 | $server = array(); 140 | $server['HTTP_HOST'] = 'amazon.com'; 141 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 142 | $server['SERVER_PORT'] = '80'; 143 | $server['REQUEST_URI'] = '/'; 144 | $server['REQUEST_METHOD'] = 'POST'; 145 | $server['CONTENT_TYPE'] = 'application/json'; 146 | 147 | $content = '{ 148 | "operation": "Purchase", 149 | "reason": "FULFILL", 150 | "productId": "GamePack1", 151 | "userId": "123456", 152 | "purchaseToken": "6f3092e5-0326-42b7-a107-416234d548d8" 153 | }'; 154 | 155 | $request = $this->generateSignedRequest($server, $content); 156 | 157 | $body = tmpfile(); 158 | fwrite($body, $content); 159 | fseek($body, 0); 160 | 161 | $controller = new AccountLinkingController(self::$credentialStore); 162 | 163 | $response = $controller->process($server, IOUtils::getFilePathFromHandle($body)); 164 | 165 | $this->assertEmpty($response); 166 | 167 | // TODO : assert headers for status 500 168 | } 169 | 170 | public function testGetUserIdInvalidSignature() 171 | { 172 | $server = array(); 173 | $server['HTTP_HOST'] = 'amazon.com'; 174 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 175 | $server['SERVER_PORT'] = '80'; 176 | $server['REQUEST_URI'] = '/'; 177 | $server['REQUEST_METHOD'] = 'POST'; 178 | $server['CONTENT_TYPE'] = 'application/json'; 179 | 180 | $content = '{ 181 | "operation": "GetUserId", 182 | "infoField1": "nobody@amazon.com", 183 | "infoField2": "amazon", 184 | "infoField3": "nobody" 185 | }'; 186 | 187 | $request = $this->generateSignedRequest($server, $content); 188 | 189 | $server['HTTP_' . HttpUtils::AUTHORIZATION_HEADER] .= "foobar"; 190 | 191 | $body = tmpfile(); 192 | fwrite($body, $content); 193 | fseek($body, 0); 194 | 195 | $controller = new AccountLinkingController(self::$credentialStore); 196 | 197 | $controller->onGetUserId(function ($req) { 198 | $res = new GetUserIdResponse(); 199 | $res->setResponse(GetUserIdResponseValue::OK); 200 | $res->setUserId('1234'); 201 | return $res; 202 | }); 203 | 204 | $response = $controller->process($server, IOUtils::getFilePathFromHandle($body)); 205 | 206 | $this->assertEmpty($response); 207 | 208 | // TODO : assert headers for status 500 209 | } 210 | 211 | public function testGetUserIdInvalidCallbackReturn() 212 | { 213 | $server = array(); 214 | $server['HTTP_HOST'] = 'amazon.com'; 215 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 216 | $server['SERVER_PORT'] = '80'; 217 | $server['REQUEST_URI'] = '/'; 218 | $server['REQUEST_METHOD'] = 'POST'; 219 | $server['CONTENT_TYPE'] = 'application/json'; 220 | 221 | $content = '{ 222 | "operation": "GetUserId", 223 | "infoField1": "nobody@amazon.com", 224 | "infoField2": "amazon", 225 | "infoField3": "nobody" 226 | }'; 227 | 228 | $request = $this->generateSignedRequest($server, $content); 229 | 230 | $body = tmpfile(); 231 | fwrite($body, $content); 232 | fseek($body, 0); 233 | 234 | $controller = new AccountLinkingController(self::$credentialStore); 235 | 236 | $controller->onGetUserId(function ($req) { 237 | return "respose"; 238 | }); 239 | 240 | $response = $controller->process($server, IOUtils::getFilePathFromHandle($body)); 241 | 242 | $this->assertEmpty($response); 243 | 244 | // TODO : assert headers for status 500 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Controllers/PurchaseControllerTest.php: -------------------------------------------------------------------------------- 1 | add($credential); 47 | 48 | self::$credential = $credential; 49 | self::$credentialStore = $credentialStore; 50 | } 51 | 52 | private function generateSignedRequest(&$server, $content) 53 | { 54 | $dateNow = new \DateTime('@' . time()); 55 | 56 | $shortDate = $dateNow->format(DateUtils::DATE_FORMAT_SHORT); 57 | $isoDate = $dateNow->format(DateUtils::DATE_FORMAT_ISO8601); 58 | 59 | $server['HTTP_' . HttpUtils::X_AMZ_DATE_HEADER] = $isoDate; 60 | 61 | $request = new Request($server, $content); 62 | 63 | $signer = new Signer(); 64 | $signer->sign($request, self::$credential); 65 | 66 | $headers = $request->getHeaders(); 67 | $server['HTTP_' . HttpUtils::AUTHORIZATION_HEADER] = $headers[HttpUtils::AUTHORIZATION_HEADER]; 68 | 69 | return $request; 70 | } 71 | 72 | public function testFulfillPurchase() 73 | { 74 | $server = array(); 75 | $server['HTTP_HOST'] = 'amazon.com'; 76 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 77 | $server['SERVER_PORT'] = '80'; 78 | $server['REQUEST_URI'] = '/'; 79 | $server['REQUEST_METHOD'] = 'POST'; 80 | $server['CONTENT_TYPE'] = 'application/json'; 81 | 82 | $content = '{ 83 | "operation": "Purchase", 84 | "reason": "FULFILL", 85 | "productId": "GamePack1", 86 | "userId": "123456", 87 | "purchaseToken": "6f3092e5-0326-42b7-a107-416234d548d8" 88 | }'; 89 | 90 | $request = $this->generateSignedRequest($server, $content); 91 | 92 | $body = tmpfile(); 93 | fwrite($body, $content); 94 | fseek($body, 0); 95 | 96 | $controller = new PurchaseController(self::$credentialStore); 97 | 98 | $controller->onFulfillPurchase(function ($req) { 99 | $res = new FulfillPurchaseResponse(); 100 | $res->setResponse(FulfillPurchaseResponseValue::FAIL_USER_NOT_ELIGIBLE); 101 | return $res; 102 | }); 103 | 104 | $response = $controller->process($server, IOUtils::getFilePathFromHandle($body)); 105 | 106 | $this->assertNotNull($response); 107 | $this->assertEquals('{"response":"FAIL_USER_NOT_ELIGIBLE"}', $response); 108 | } 109 | 110 | public function testRevokePurchase() 111 | { 112 | $server = array(); 113 | $server['HTTP_HOST'] = 'amazon.com'; 114 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 115 | $server['SERVER_PORT'] = '80'; 116 | $server['REQUEST_URI'] = '/'; 117 | $server['REQUEST_METHOD'] = 'POST'; 118 | $server['CONTENT_TYPE'] = 'application/json'; 119 | 120 | $content = '{ 121 | "operation": "Revoke", 122 | "reason": "CUSTOMER_SERVICE_REQUEST", 123 | "productId": "GamePack1", 124 | "userId": "123456", 125 | "purchaseToken": "6f3092e5-0326-42b7-a107-416234d548d8" 126 | }'; 127 | 128 | $request = $this->generateSignedRequest($server, $content); 129 | 130 | $body = tmpfile(); 131 | fwrite($body, $content); 132 | fseek($body, 0); 133 | 134 | $controller = new PurchaseController(self::$credentialStore); 135 | 136 | $controller->onRevokePurchase(function ($req) { 137 | $res = new RevokePurchaseResponse(); 138 | $res->setResponse(RevokePurchaseResponseValue::FAIL_INVALID_PURCHASE_TOKEN); 139 | return $res; 140 | }); 141 | 142 | $response = $controller->process($server, IOUtils::getFilePathFromHandle($body)); 143 | 144 | $this->assertNotNull($response); 145 | $this->assertEquals('{"response":"FAIL_INVALID_PURCHASE_TOKEN"}', $response); 146 | } 147 | 148 | public function testSubscriptionActivate() 149 | { 150 | $server = array(); 151 | $server['HTTP_HOST'] = 'amazon.com'; 152 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 153 | $server['SERVER_PORT'] = '80'; 154 | $server['REQUEST_URI'] = '/'; 155 | $server['REQUEST_METHOD'] = 'POST'; 156 | $server['CONTENT_TYPE'] = 'application/json'; 157 | 158 | $content = '{ 159 | "operation": "SubscriptionActivate", 160 | "subscriptionId": "subscriptionId", 161 | "productId": "GamePack1", 162 | "userId": "1234" 163 | }'; 164 | 165 | $request = $this->generateSignedRequest($server, $content); 166 | 167 | $body = tmpfile(); 168 | fwrite($body, $content); 169 | fseek($body, 0); 170 | 171 | $controller = new PurchaseController(self::$credentialStore); 172 | 173 | $controller->onSubscriptionActivate(function ($req) { 174 | $res = new SubscriptionActivateResponse(); 175 | $res->setResponse(SubscriptionActivateResponseValue::FAIL_USER_INVALID); 176 | return $res; 177 | }); 178 | 179 | $response = $controller->process($server, IOUtils::getFilePathFromHandle($body)); 180 | 181 | $this->assertNotNull($response); 182 | $this->assertEquals('{"response":"FAIL_USER_INVALID"}', $response); 183 | } 184 | 185 | public function testSubscriptionActivateTeamSubsV1() 186 | { 187 | $server = array(); 188 | $server['HTTP_HOST'] = 'amazon.com'; 189 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 190 | $server['SERVER_PORT'] = '80'; 191 | $server['REQUEST_URI'] = '/'; 192 | $server['REQUEST_METHOD'] = 'POST'; 193 | $server['CONTENT_TYPE'] = 'application/json'; 194 | 195 | $content = '{ 196 | "operation": "SubscriptionActivate", 197 | "subscriptionId": "subscriptionId", 198 | "productId": "GamePack1", 199 | "userId": "1234", 200 | "subscriptionGroupId": "group1234", 201 | "numberOfSubscriptionsInGroup": "4" 202 | }'; 203 | 204 | $request = $this->generateSignedRequest($server, $content); 205 | 206 | $body = tmpfile(); 207 | fwrite($body, $content); 208 | fseek($body, 0); 209 | 210 | $controller = new PurchaseController(self::$credentialStore); 211 | 212 | $controller->onSubscriptionActivate(function ($req) { 213 | $res = new SubscriptionActivateResponse(); 214 | $res->setResponse(SubscriptionActivateResponseValue::FAIL_INVALID_SUBSCRIPTION); 215 | return $res; 216 | }); 217 | 218 | $response = $controller->process($server, IOUtils::getFilePathFromHandle($body)); 219 | 220 | $this->assertNotNull($response); 221 | $this->assertEquals('{"response":"FAIL_INVALID_SUBSCRIPTION"}', $response); 222 | } 223 | 224 | public function testSubscriptionDeactivate() 225 | { 226 | $server = array(); 227 | $server['HTTP_HOST'] = 'amazon.com'; 228 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 229 | $server['SERVER_PORT'] = '80'; 230 | $server['REQUEST_URI'] = '/'; 231 | $server['REQUEST_METHOD'] = 'POST'; 232 | $server['CONTENT_TYPE'] = 'application/json'; 233 | 234 | $content = '{ 235 | "operation": "SubscriptionDeactivate", 236 | "subscriptionId": "subscriptionId", 237 | "reason": "PAYMENT_PROBLEM", 238 | "period": "REGULAR" 239 | }'; 240 | 241 | $request = $this->generateSignedRequest($server, $content); 242 | 243 | $body = tmpfile(); 244 | fwrite($body, $content); 245 | fseek($body, 0); 246 | 247 | $controller = new PurchaseController(self::$credentialStore); 248 | 249 | $controller->onSubscriptionDeactivate(function ($req) { 250 | $res = new SubscriptionDeactivateResponse(); 251 | $res->setResponse(SubscriptionDeactivateResponseValue::OK); 252 | return $res; 253 | }); 254 | 255 | $response = $controller->process($server, IOUtils::getFilePathFromHandle($body)); 256 | 257 | $this->assertNotNull($response); 258 | $this->assertEquals('{"response":"OK"}', $response); 259 | } 260 | 261 | public function testProcessOperationInvalidOperation() 262 | { 263 | $server = array(); 264 | $server['HTTP_HOST'] = 'amazon.com'; 265 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 266 | $server['SERVER_PORT'] = '80'; 267 | $server['REQUEST_URI'] = '/'; 268 | $server['REQUEST_METHOD'] = 'POST'; 269 | $server['CONTENT_TYPE'] = 'application/json'; 270 | 271 | $content = '{ 272 | "operation": "GetUserId", 273 | "infoField1": "nobody@amazon.com", 274 | "infoField2": "amazon", 275 | "infoField3": "nobody" 276 | }'; 277 | 278 | $request = $this->generateSignedRequest($server, $content); 279 | 280 | $body = tmpfile(); 281 | fwrite($body, $content); 282 | fseek($body, 0); 283 | 284 | $controller = new PurchaseController(self::$credentialStore); 285 | 286 | $controller->onSubscriptionDeactivate(function ($req) { 287 | $res = new SubscriptionDeactivateResponse(); 288 | $res->setResponse(SubscriptionDeactivateResponseValue::OK); 289 | return $res; 290 | }); 291 | 292 | $response = $controller->process($server, IOUtils::getFilePathFromHandle($body)); 293 | $this->assertEmpty($response); 294 | 295 | // TODO : assert headers for status 500 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Serialization/Enums/InstantAccessOperationValueTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(InstantAccessOperationValue::isValid(InstantAccessOperationValue::PURCHASE)); 23 | $this->assertTrue(InstantAccessOperationValue::isValid(InstantAccessOperationValue::REVOKE)); 24 | $this->assertTrue(InstantAccessOperationValue::isValid(InstantAccessOperationValue::GET_USER_ID)); 25 | $this->assertTrue(InstantAccessOperationValue::isValid(InstantAccessOperationValue::SUBSCRIPTION_ACTIVATE)); 26 | $this->assertTrue(InstantAccessOperationValue::isValid(InstantAccessOperationValue::SUBSCRIPTION_DEACTIVATE)); 27 | } 28 | 29 | public function testInvalidValues() 30 | { 31 | $this->assertFalse(InstantAccessOperationValue::isValid("test")); 32 | $this->assertFalse(InstantAccessOperationValue::isValid("")); 33 | $this->assertFalse(InstantAccessOperationValue::isValid(null)); 34 | $this->assertFalse(InstantAccessOperationValue::isValid(array())); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Serialization/FulfillPurchaseRequestTest.php: -------------------------------------------------------------------------------- 1 | assertNotNull($fulfillRequest); 36 | $this->assertEquals(InstantAccessOperationValue::PURCHASE, $fulfillRequest->getOperation()); 37 | $this->assertEquals(FulfillPurchaseReasonValue::FULFILL, $fulfillRequest->getReason()); 38 | $this->assertEquals('GamePack1', $fulfillRequest->getProductId()); 39 | $this->assertEquals('123456', $fulfillRequest->getUserId()); 40 | $this->assertEquals('6f3092e5-0326-42b7-a107-416234d548d8', $fulfillRequest->getPurchaseToken()); 41 | } 42 | 43 | public function testCreateFromJsonInvalidReason() 44 | { 45 | $this->setExpectedException('InvalidArgumentException'); 46 | 47 | $json = '{ 48 | "operation": "Purchase", 49 | "reason": "PURCHASE", 50 | "productId": "GamePack1", 51 | "userId": "123456", 52 | "purchaseToken": "6f3092e5-0326-42b7-a107-416234d548d8" 53 | }'; 54 | 55 | $fulfillRequest = FulfillPurchaseRequest::createFromJson($json); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Serialization/FulfillPurchaseResponseTest.php: -------------------------------------------------------------------------------- 1 | setResponse(FulfillPurchaseResponseValue::OK); 26 | 27 | $json = $fulfillResponse->toJson(); 28 | 29 | $this->assertNotEmpty($fulfillResponse); 30 | $this->assertEquals('{"response":"OK"}', $json); 31 | } 32 | 33 | public function testToJsonInvalidResponse() 34 | { 35 | $this->setExpectedException('InvalidArgumentException'); 36 | 37 | $fulfillResponse = new FulfillPurchaseResponse(); 38 | $fulfillResponse->setResponse('foobar'); 39 | 40 | $json = $fulfillResponse->toJson(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Serialization/GetUserIdRequestTest.php: -------------------------------------------------------------------------------- 1 | assertNotNull($getUserIdRequest); 34 | $this->assertEquals(InstantAccessOperationValue::GET_USER_ID, $getUserIdRequest->getOperation()); 35 | $this->assertEquals('nobody@amazon.com', $getUserIdRequest->getInfoField1()); 36 | $this->assertEquals('amazon', $getUserIdRequest->getInfoField2()); 37 | $this->assertEquals('nobody', $getUserIdRequest->getInfoField3()); 38 | } 39 | 40 | public function testCreateFromJsonWithOneInfoField() 41 | { 42 | $json = '{ 43 | "operation": "GetUserId", 44 | "infoField1": "nobody@amazon.com" 45 | }'; 46 | 47 | $getUserIdRequest = GetUserIdRequest::createFromJson($json); 48 | 49 | $this->assertNotNull($getUserIdRequest); 50 | $this->assertEquals(InstantAccessOperationValue::GET_USER_ID, $getUserIdRequest->getOperation()); 51 | $this->assertEquals('nobody@amazon.com', $getUserIdRequest->getInfoField1()); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Serialization/GetUserIdResponseTest.php: -------------------------------------------------------------------------------- 1 | setResponse(GetUserIdResponseValue::OK); 26 | $getUserIdResponse->setUserId('1234'); 27 | 28 | $json = $getUserIdResponse->toJson(); 29 | 30 | $this->assertNotEmpty($getUserIdResponse); 31 | $this->assertEquals('{"userId":"1234","response":"OK"}', $json); 32 | } 33 | 34 | public function testToJsonInvalidResponse() 35 | { 36 | $this->setExpectedException('InvalidArgumentException'); 37 | 38 | $getUserIdResponse = new GetUserIdResponse(); 39 | $getUserIdResponse->setResponse('foobar'); 40 | 41 | $json = $getUserIdResponse->toJson(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Serialization/InstantAccessRequestTest.php: -------------------------------------------------------------------------------- 1 | assertNotNull($iaRequest); 31 | $this->assertEquals(InstantAccessOperationValue::PURCHASE, $iaRequest->getOperation()); 32 | } 33 | 34 | public function testCreateFromJsonInvalidValue() 35 | { 36 | $this->setExpectedException('InvalidArgumentException'); 37 | 38 | $json = '{ 39 | "operation" : "FooBar" 40 | }'; 41 | 42 | $iaRequest = InstantAccessRequest::createFromJson($json); 43 | } 44 | 45 | public function testCreateFromJsonInvalidJson() 46 | { 47 | $this->setExpectedException('InvalidArgumentException'); 48 | 49 | $json = '{ 50 | "operation" : "Purchase" 51 | '; 52 | 53 | $iaRequest = InstantAccessRequest::createFromJson($json); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Serialization/InstantAccessResponseTest.php: -------------------------------------------------------------------------------- 1 | setResponse("OK"); 24 | 25 | $json = $iaResponse->toJson(); 26 | 27 | $this->assertNotEmpty($iaResponse); 28 | $this->assertEquals('{"response":"OK"}', $json); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Serialization/RevokePurchaseRequestTest.php: -------------------------------------------------------------------------------- 1 | assertNotNull($revokeRequest); 36 | $this->assertEquals(InstantAccessOperationValue::REVOKE, $revokeRequest->getOperation()); 37 | $this->assertEquals(RevokePurchaseReasonValue::CUSTOMER_SERVICE_REQUEST, $revokeRequest->getReason()); 38 | $this->assertEquals('GamePack1', $revokeRequest->getProductId()); 39 | $this->assertEquals('123456', $revokeRequest->getUserId()); 40 | $this->assertEquals('6f3092e5-0326-42b7-a107-416234d548d8', $revokeRequest->getPurchaseToken()); 41 | } 42 | 43 | public function testCreateFromJsonInvalidReason() 44 | { 45 | $this->setExpectedException('InvalidArgumentException'); 46 | 47 | $json = '{ 48 | "operation": "Revoke", 49 | "reason": "USER_MISCLICK", 50 | "productId": "GamePack1", 51 | "userId": "123456", 52 | "purchaseToken": "6f3092e5-0326-42b7-a107-416234d548d8" 53 | }'; 54 | 55 | $revokeRequest = RevokePurchaseRequest::createFromJson($json); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Serialization/RevokePurchaseResponseTest.php: -------------------------------------------------------------------------------- 1 | setResponse(RevokePurchaseResponseValue::FAIL_INVALID_PURCHASE_TOKEN); 26 | 27 | $json = $revokeResponse->toJson(); 28 | 29 | $this->assertNotEmpty($revokeResponse); 30 | $this->assertEquals('{"response":"FAIL_INVALID_PURCHASE_TOKEN"}', $json); 31 | } 32 | 33 | public function testToJsonInvalidResponse() 34 | { 35 | $this->setExpectedException('InvalidArgumentException'); 36 | 37 | $revokeResponse = new RevokePurchaseResponse(); 38 | $revokeResponse->setResponse('foobar'); 39 | 40 | $json = $revokeResponse->toJson(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Serialization/SubscriptionActivateRequestTest.php: -------------------------------------------------------------------------------- 1 | assertNotNull($subRequest); 34 | $this->assertEquals(InstantAccessOperationValue::SUBSCRIPTION_ACTIVATE, $subRequest->getOperation()); 35 | $this->assertEquals('subscriptionId', $subRequest->getSubscriptionId()); 36 | $this->assertEquals('GamePack1', $subRequest->getProductId()); 37 | $this->assertEquals('1234', $subRequest->getUserId()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Serialization/SubscriptionActivateResponseTest.php: -------------------------------------------------------------------------------- 1 | setResponse(SubscriptionActivateResponseValue::FAIL_USER_INVALID); 26 | 27 | $json = $subResponse->toJson(); 28 | 29 | $this->assertNotEmpty($subResponse); 30 | $this->assertEquals('{"response":"FAIL_USER_INVALID"}', $json); 31 | } 32 | 33 | public function testToJsonInvalidResponse() 34 | { 35 | $this->setExpectedException('InvalidArgumentException'); 36 | 37 | $subResponse = new SubscriptionActivateResponse(); 38 | $subResponse->setResponse('foobar'); 39 | 40 | $json = $subResponse->toJson(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Serialization/SubscriptionDeactivateRequestTest.php: -------------------------------------------------------------------------------- 1 | assertNotNull($subRequest); 36 | $this->assertEquals(InstantAccessOperationValue::SUBSCRIPTION_DEACTIVATE, $subRequest->getOperation()); 37 | $this->assertEquals('subscriptionId', $subRequest->getSubscriptionId()); 38 | $this->assertEquals(SubscriptionDeactivateReasonValue::USER_REQUEST, $subRequest->getReason()); 39 | $this->assertEquals(SubscriptionDeactivatePeriodValue::REGULAR, $subRequest->getPeriod()); 40 | } 41 | 42 | public function testCreateFromJsonInvalidReason() 43 | { 44 | $this->setExpectedException('InvalidArgumentException'); 45 | 46 | $json = '{ 47 | "operation": "SubscriptionDeactivate", 48 | "subscriptionId": "subscriptionId", 49 | "reason": "UNKNOWN", 50 | "period": "REGULAR" 51 | }'; 52 | 53 | $subRequest = SubscriptionDeactivateRequest::createFromJson($json); 54 | } 55 | 56 | public function testCreateFromJsonInvalidPeriod() 57 | { 58 | $this->setExpectedException('InvalidArgumentException'); 59 | 60 | $json = '{ 61 | "operation": "SubscriptionDeactivate", 62 | "subscriptionId": "subscriptionId", 63 | "reason": "USER_REQUEST", 64 | "period": "NEGATIVE" 65 | }'; 66 | 67 | $subRequest = SubscriptionDeactivateRequest::createFromJson($json); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Serialization/SubscriptionDeactivateResponseTest.php: -------------------------------------------------------------------------------- 1 | setResponse(SubscriptionDeactivateResponseValue::OK); 26 | 27 | $json = $subResponse->toJson(); 28 | 29 | $this->assertNotEmpty($subResponse); 30 | $this->assertEquals('{"response":"OK"}', $json); 31 | } 32 | 33 | public function testToJsonInvalidResponse() 34 | { 35 | $this->setExpectedException('InvalidArgumentException'); 36 | 37 | $subResponse = new SubscriptionDeactivateResponse(); 38 | $subResponse->setResponse('foobar'); 39 | 40 | $json = $subResponse->toJson(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Signature/AuthorizationHeaderTest.php: -------------------------------------------------------------------------------- 1 | assertNotNull($header); 30 | $this->assertEquals('DTA1-HMAC-SHA256', $header->getAlgorithm()); 31 | $this->assertEquals(array('key' => 'KEYID', 'date' =>'20110909'), $header->getCredential()); 32 | $this->assertEquals(array('aaa', 'content-type', 'x-amz-date', 'zzz'), $header->getSignedHeaders()); 33 | $this->assertEquals('87729cb3475859a18b5d9cead0bba82f0f56a85c2a13bed3bc229c6c35e06628', $header->getSignature()); 34 | } 35 | 36 | public function testParseValidHeader2() 37 | { 38 | $value = 'DTA1-HMAC-SHA256 ' . 39 | 'SignedHeaders=Content-Type;X-Amz-Date;X-Amz-Dta-Version;X-AMZ-REQUEST-ID, ' . 40 | 'Credential=367caa91-cde5-48f2-91fe-bb95f546e9f0/20131207, ' . 41 | 'Signature=6fe5d5bbf4acda9b0f47f66db3ad8f23a33117ee52b45ae69983bec0b50550fe'; 42 | 43 | $header = AuthorizationHeader::parse($value); 44 | 45 | $this->assertNotNull($header); 46 | $this->assertEquals('DTA1-HMAC-SHA256', $header->getAlgorithm()); 47 | $this->assertEquals(array('key' => '367caa91-cde5-48f2-91fe-bb95f546e9f0', 'date' =>'20131207'), $header->getCredential()); 48 | $this->assertEquals(array('content-type', 'x-amz-date', 'x-amz-dta-version', 'x-amz-request-id'), $header->getSignedHeaders()); 49 | $this->assertEquals('6fe5d5bbf4acda9b0f47f66db3ad8f23a33117ee52b45ae69983bec0b50550fe', $header->getSignature()); 50 | } 51 | 52 | public function testParseInvalidHeader() 53 | { 54 | $this->setExpectedException('\InvalidArgumentException'); 55 | 56 | $value = ''; 57 | 58 | $header = AuthorizationHeader::parse($value); 59 | } 60 | 61 | public function testParseInvalidHeaderFormat() 62 | { 63 | $this->setExpectedException('\InvalidArgumentException'); 64 | 65 | $value = 'SignedHeaders=content-type;x-amz-date;x-amz-dta-version;x-amz-request-id, ' . 66 | 'Credential=367caa91-cde5-48f2-91fe-bb95f546e9f0, ' . 67 | 'Signature=6fe5d5bbf4acda9b0f47f66db3ad8f23a33117ee52b45ae69983bec0b50550fe'; 68 | 69 | $header = AuthorizationHeader::parse($value); 70 | } 71 | 72 | public function testParseInvalidHeaderinvalidCredential() 73 | { 74 | $this->setExpectedException('\InvalidArgumentException'); 75 | 76 | $value = 'DTA1-HMAC-SHA256 ' . 77 | 'SignedHeaders=content-type;x-amz-date;x-amz-dta-version;x-amz-request-id, ' . 78 | 'Credential=367caa91-cde5-48f2-91fe-bb95f546e9f0, ' . 79 | 'Signature=6fe5d5bbf4acda9b0f47f66db3ad8f23a33117ee52b45ae69983bec0b50550fe'; 80 | 81 | $header = AuthorizationHeader::parse($value); 82 | } 83 | 84 | public function testToString() 85 | { 86 | $header = new AuthorizationHeader( 87 | 'DTA1-HMAC-SHA256', 88 | array('content-type', 'x-amz-date', 'x-amz-dta-version', 'x-amz-request-id'), 89 | array('key' => '367caa91-cde5-48f2-91fe-bb95f546e9f0', 'date' => '20140101'), 90 | '6fe5d5bbf4acda9b0f47f66db3ad8f23a33117ee52b45ae69983bec0b50550fe' 91 | ); 92 | 93 | $str = 'DTA1-HMAC-SHA256 ' . 94 | 'SignedHeaders=content-type;x-amz-date;x-amz-dta-version;x-amz-request-id, ' . 95 | 'Credential=367caa91-cde5-48f2-91fe-bb95f546e9f0/20140101, ' . 96 | 'Signature=6fe5d5bbf4acda9b0f47f66db3ad8f23a33117ee52b45ae69983bec0b50550fe'; 97 | 98 | $this->assertEquals($str, (string)$header); 99 | } 100 | 101 | public function testCreateAuthorixationHeaderWithInvalidAlgorithm() 102 | { 103 | $this->setExpectedException('\InvalidArgumentException'); 104 | 105 | $header = new AuthorizationHeader( 106 | '', 107 | array('content-type', 'x-amz-date', 'x-amz-dta-version', 'x-amz-request-id'), 108 | array('key' => '367caa91-cde5-48f2-91fe-bb95f546e9f0', 'date' => '20140101'), 109 | '6fe5d5bbf4acda9b0f47f66db3ad8f23a33117ee52b45ae69983bec0b50550fe' 110 | ); 111 | } 112 | 113 | public function testCreateAuthorixationHeaderWithInvalidHeaders() 114 | { 115 | $this->setExpectedException('\InvalidArgumentException'); 116 | 117 | $header = new AuthorizationHeader( 118 | 'DTA1-HMAC-SHA256', 119 | 'test', 120 | array('key' => '367caa91-cde5-48f2-91fe-bb95f546e9f0', 'date' => '20140101'), 121 | '6fe5d5bbf4acda9b0f47f66db3ad8f23a33117ee52b45ae69983bec0b50550fe' 122 | ); 123 | } 124 | 125 | public function testCreateAuthorixationHeaderWithInvalidCredential() 126 | { 127 | $this->setExpectedException('\InvalidArgumentException'); 128 | 129 | $header = new AuthorizationHeader( 130 | 'DTA1-HMAC-SHA256', 131 | array('content-type', 'x-amz-date', 'x-amz-dta-version', 'x-amz-request-id'), 132 | array('key' => '367caa91-cde5-48f2-91fe-bb95f546e9f0'), 133 | '6fe5d5bbf4acda9b0f47f66db3ad8f23a33117ee52b45ae69983bec0b50550fe' 134 | ); 135 | } 136 | 137 | public function testCreateAuthorixationHeaderWithInvalidSignature() 138 | { 139 | $this->setExpectedException('\InvalidArgumentException'); 140 | 141 | $header = new AuthorizationHeader( 142 | 'DTA1-HMAC-SHA256', 143 | array('content-type', 'x-amz-date', 'x-amz-dta-version', 'x-amz-request-id'), 144 | array('key' => '367caa91-cde5-48f2-91fe-bb95f546e9f0', 'date' => '20140101'), 145 | '' 146 | ); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Signature/CredentialStoreTest.php: -------------------------------------------------------------------------------- 1 | loadFromFile(IOUtils::getFilePathFromHandle(self::$VALID_FILE)); 52 | 53 | $this->assertCorrectCredentials($store); 54 | } 55 | 56 | public function testLoad() 57 | { 58 | $store = new CredentialStore(); 59 | 60 | $contents = file_get_contents(IOUtils::getFilePathFromHandle(self::$VALID_FILE)); 61 | $store->load($contents); 62 | 63 | $this->assertCorrectCredentials($store); 64 | } 65 | 66 | public function testLoadFromEmptyString() 67 | { 68 | $this->setExpectedException('InvalidArgumentException'); 69 | 70 | $store = new CredentialStore(); 71 | $store->load(null); 72 | } 73 | 74 | public function testLoadFromInvalidFile() 75 | { 76 | $this->setExpectedException('InvalidArgumentException'); 77 | 78 | $store = new CredentialStore(); 79 | $store->loadFromFile(IOUtils::getFilePathFromHandle(self::$INVALID_FILE)); 80 | } 81 | 82 | public function testLoadFromEmptyFile() 83 | { 84 | $this->setExpectedException('InvalidArgumentException'); 85 | 86 | $store = new CredentialStore(); 87 | $store->loadFromFile(null); 88 | } 89 | 90 | public function testGetInvalidCredential() 91 | { 92 | $store = new CredentialStore(); 93 | $store->loadFromFile(IOUtils::getFilePathFromHandle(self::$VALID_FILE)); 94 | $credential = $store->get(self::$INVALID_KEY); 95 | 96 | $this->assertNull($credential); 97 | } 98 | 99 | public function testAddCredential() 100 | { 101 | $store = new CredentialStore(); 102 | $store->add(new Credential(self::$KEYS[0], self::$KEYS[1])); 103 | 104 | $credential = $store->get(self::$KEYS[1]); 105 | 106 | $this->assertEquals(self::$KEYS[0], $credential->getSecretKey()); 107 | $this->assertEquals(self::$KEYS[1], $credential->getPublicKey()); 108 | } 109 | 110 | public function testRemoveCredential() 111 | { 112 | $store = new CredentialStore(); 113 | $store->add(new Credential(self::$KEYS[0], self::$KEYS[1])); 114 | 115 | $this->assertEquals(1, count($store->getAll())); 116 | 117 | $store->remove(self::$KEYS[1]); 118 | 119 | $this->assertEquals(0, count($store->getAll())); 120 | } 121 | 122 | private function assertCorrectCredentials($store) 123 | { 124 | $this->assertEquals(self::$KEYS[0], $store->get(self::$KEYS[1])->getSecretKey()); 125 | $this->assertEquals(self::$KEYS[1], $store->get(self::$KEYS[1])->getPublicKey()); 126 | 127 | $this->assertEquals(self::$KEYS[2], $store->get(self::$KEYS[3])->getSecretKey()); 128 | $this->assertEquals(self::$KEYS[3], $store->get(self::$KEYS[3])->getPublicKey()); 129 | 130 | $this->assertEquals(self::$KEYS[4], $store->get(self::$KEYS[5])->getSecretKey()); 131 | $this->assertEquals(self::$KEYS[5], $store->get(self::$KEYS[5])->getPublicKey()); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Signature/CredentialTest.php: -------------------------------------------------------------------------------- 1 | setExpectedException('InvalidArgumentException'); 23 | 24 | $credential = new Credential(null, 'PUBLIC'); 25 | } 26 | 27 | public function testCreateCredentialWithInvalidArguments2() 28 | { 29 | $this->setExpectedException('InvalidArgumentException'); 30 | 31 | $credential = new Credential('SECRET', ''); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Signature/RequestTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('http://amazon.com/index.html?a=1', $request->getUrl()); 37 | $this->assertEquals('POST', $request->getMethod()); 38 | $this->assertEquals('{}', $request->getBody()); 39 | 40 | $this->assertEquals(array('host' => 'amazon.com', 'accept' => '*/*'), $request->getHeaders()); 41 | } 42 | 43 | public function testFilterHeaders() 44 | { 45 | $server = array(); 46 | $server['HTTP_HOST'] = 'amazon.com'; 47 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 48 | $server['SERVER_PORT'] = '80'; 49 | $server['REQUEST_URI'] = '/index.html?a=1'; 50 | $server['REQUEST_METHOD'] = 'POST'; 51 | $server['HTTP_ACCEPT'] = '*/*'; 52 | 53 | $request = new Request($server); 54 | 55 | $filter = array('host'); 56 | 57 | $request->filterHeaders($filter); 58 | 59 | $this->assertEquals(1, count($request->getHeaders())); 60 | $this->assertEquals(array('accept' => '*/*'), $request->getHeaders()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Signature/SignerTest.php: -------------------------------------------------------------------------------- 1 | format(DateUtils::DATE_FORMAT_SHORT); 29 | $isoDate = $dateNow->format(DateUtils::DATE_FORMAT_ISO8601); 30 | 31 | $server = array(); 32 | $server['HTTP_HOST'] = 'amazon.com'; 33 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 34 | $server['SERVER_PORT'] = '80'; 35 | $server['REQUEST_URI'] = '/'; 36 | $server['REQUEST_METHOD'] = 'GET'; 37 | $server['CONTENT_TYPE'] = 'application/json'; 38 | $server['HTTP_' . HttpUtils::X_AMZ_DATE_HEADER] = $isoDate; 39 | 40 | $content = 'body'; 41 | 42 | $request = new Request($server, $content); 43 | 44 | $credential = new Credential('SECRETKEY', 'KEYID'); 45 | $store = new CredentialStore(); 46 | $store->add($credential); 47 | 48 | $signer = new Signer(); 49 | 50 | // remove the host header because this signature was generated by the Java SDK 51 | // and it does not automatically adds this header 52 | $headers = &$request->getHeaders(); 53 | unset($headers['host']); 54 | 55 | $header = $signer->getAuthorizationHeader($request, $credential, $shortDate, $isoDate); 56 | 57 | $this->assertEquals('DTA1-HMAC-SHA256 SignedHeaders=content-type;x-amz-date, Credential=KEYID/20110909, Signature=4d2f81ea2cf8d6963f8176a22eec4c65ae95c63502326a7c148686da7d50f47e', $header); 58 | } 59 | 60 | public function testSignAndVerify() 61 | { 62 | $server = array(); 63 | $server['HTTP_HOST'] = 'amazon.com'; 64 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 65 | $server['SERVER_PORT'] = '80'; 66 | $server['REQUEST_URI'] = '/service/foo.php'; 67 | $server['REQUEST_METHOD'] = 'POST'; 68 | $server['HTTP_ACCEPT'] = '*/*'; 69 | $server['CONTENT_TYPE'] = 'application/json'; 70 | 71 | $content = '{"operation" : "bar"}'; 72 | 73 | $request = new Request($server, $content); 74 | 75 | $credential = new Credential('SECRET', 'PUBLIC'); 76 | $store = new CredentialStore(); 77 | $store->add($credential); 78 | 79 | $signer = new Signer(); 80 | 81 | $signer->sign($request, $credential); 82 | 83 | $verified = $signer->verify($request, $store); 84 | 85 | $this->assertTrue($verified); 86 | } 87 | 88 | public function testVerifyEmptyCredentialStore() 89 | { 90 | $this->setExpectedException('InvalidArgumentException'); 91 | 92 | $server = array(); 93 | $server['HTTP_HOST'] = 'amazon.com'; 94 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 95 | $server['SERVER_PORT'] = '80'; 96 | $server['REQUEST_URI'] = '/service/foo.php'; 97 | $server['REQUEST_METHOD'] = 'POST'; 98 | $server['HTTP_ACCEPT'] = '*/*'; 99 | $server['CONTENT_TYPE'] = 'application/json'; 100 | 101 | $content = '{"operation" : "bar"}'; 102 | 103 | $request = new Request($server, $content); 104 | 105 | $signer = new Signer(); 106 | $signer->verify($request, new CredentialStore()); 107 | } 108 | 109 | public function testVerifyRequestNoDate() 110 | { 111 | $server = array(); 112 | $server['HTTP_HOST'] = 'amazon.com'; 113 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 114 | $server['SERVER_PORT'] = '80'; 115 | $server['REQUEST_URI'] = '/'; 116 | $server['REQUEST_METHOD'] = 'GET'; 117 | $server['CONTENT_TYPE'] = 'application/json'; 118 | 119 | $content = 'body'; 120 | 121 | $request = new Request($server, $content); 122 | 123 | $credential = new Credential('SECRETKEY', 'KEYID'); 124 | $store = new CredentialStore(); 125 | $store->add($credential); 126 | 127 | $signer = new Signer(); 128 | 129 | $this->assertFalse($signer->verify($request, $store)); 130 | } 131 | 132 | public function testVerifyRequestNoAuthorization() 133 | { 134 | $server = array(); 135 | $server['HTTP_HOST'] = 'amazon.com'; 136 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 137 | $server['SERVER_PORT'] = '80'; 138 | $server['REQUEST_URI'] = '/'; 139 | $server['REQUEST_METHOD'] = 'GET'; 140 | $server['CONTENT_TYPE'] = 'application/json'; 141 | 142 | $dateNow = new \DateTime('@' . time()); 143 | $server['HTTP_' . HttpUtils::X_AMZ_DATE_HEADER] = $dateNow->format(DateUtils::DATE_FORMAT_ISO8601); 144 | 145 | $content = 'body'; 146 | 147 | $request = new Request($server, $content); 148 | 149 | $credential = new Credential('SECRETKEY', 'KEYID'); 150 | $store = new CredentialStore(); 151 | $store->add($credential); 152 | 153 | $signer = new Signer(); 154 | 155 | $this->assertFalse($signer->verify($request, $store)); 156 | } 157 | 158 | public function testVerifyRequestWithInvalidAuthorization() 159 | { 160 | $server = array(); 161 | $server['HTTP_HOST'] = 'amazon.com'; 162 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 163 | $server['SERVER_PORT'] = '80'; 164 | $server['REQUEST_URI'] = '/'; 165 | $server['REQUEST_METHOD'] = 'GET'; 166 | $server['CONTENT_TYPE'] = 'application/json'; 167 | 168 | $dateNow = new \DateTime('@' . time()); 169 | $server['HTTP_' . HttpUtils::X_AMZ_DATE_HEADER] = $dateNow->format(DateUtils::DATE_FORMAT_ISO8601); 170 | 171 | $server['HTTP_' . HttpUtils::AUTHORIZATION_HEADER] = 'foo'; 172 | 173 | $content = 'body'; 174 | 175 | $request = new Request($server, $content); 176 | 177 | $credential = new Credential('SECRETKEY', 'KEYID'); 178 | $store = new CredentialStore(); 179 | $store->add($credential); 180 | 181 | $signer = new Signer(); 182 | 183 | $this->assertFalse($signer->verify($request, $store)); 184 | } 185 | 186 | public function testVerifyRequestWithLateMessage() 187 | { 188 | $server = array(); 189 | $server['HTTP_HOST'] = 'amazon.com'; 190 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 191 | $server['SERVER_PORT'] = '80'; 192 | $server['REQUEST_URI'] = '/'; 193 | $server['REQUEST_METHOD'] = 'GET'; 194 | $server['CONTENT_TYPE'] = 'application/json'; 195 | 196 | $dateOld = \DateTime::createFromFormat(DateUtils::DATE_FORMAT_ISO8601, '20110909T233600Z'); 197 | $server['HTTP_' . HttpUtils::X_AMZ_DATE_HEADER] = $dateOld->format(DateUtils::DATE_FORMAT_ISO8601); 198 | 199 | $server['HTTP_' . HttpUtils::AUTHORIZATION_HEADER] = 'DTA1-HMAC-SHA256 SignedHeaders=xxx, Credential=KEYID/20110909, Signature=xxx'; 200 | 201 | $content = 'body'; 202 | 203 | $request = new Request($server, $content); 204 | 205 | $credential = new Credential('SECRETKEY', 'KEYID'); 206 | $store = new CredentialStore(); 207 | $store->add($credential); 208 | 209 | $signer = new Signer(); 210 | 211 | $this->assertFalse($signer->verify($request, $store)); 212 | } 213 | 214 | public function testVerifyRequestWithInvalidCredential() 215 | { 216 | $server = array(); 217 | $server['HTTP_HOST'] = 'amazon.com'; 218 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 219 | $server['SERVER_PORT'] = '80'; 220 | $server['REQUEST_URI'] = '/'; 221 | $server['REQUEST_METHOD'] = 'GET'; 222 | $server['CONTENT_TYPE'] = 'application/json'; 223 | 224 | $dateNow = new \DateTime('@' . time()); 225 | $server['HTTP_' . HttpUtils::X_AMZ_DATE_HEADER] = $dateNow->format(DateUtils::DATE_FORMAT_ISO8601); 226 | 227 | $server['HTTP_' . HttpUtils::AUTHORIZATION_HEADER] = 'DTA1-HMAC-SHA256 SignedHeaders=xxx, Credential=NOTKEYID/' . $dateNow->format(DateUtils::DATE_FORMAT_SHORT) . ', Signature=xxx'; 228 | 229 | $content = 'body'; 230 | 231 | $request = new Request($server, $content); 232 | 233 | $credential = new Credential('SECRETKEY', 'KEYID'); 234 | $store = new CredentialStore(); 235 | $store->add($credential); 236 | 237 | $signer = new Signer(); 238 | 239 | $this->assertFalse($signer->verify($request, $store)); 240 | } 241 | 242 | public function testVerifyRequestWithInvalidCredentialDate() 243 | { 244 | $server = array(); 245 | $server['HTTP_HOST'] = 'amazon.com'; 246 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 247 | $server['SERVER_PORT'] = '80'; 248 | $server['REQUEST_URI'] = '/'; 249 | $server['REQUEST_METHOD'] = 'GET'; 250 | $server['CONTENT_TYPE'] = 'application/json'; 251 | 252 | $dateNow = new \DateTime('@' . time()); 253 | $server['HTTP_' . HttpUtils::X_AMZ_DATE_HEADER] = $dateNow->format(DateUtils::DATE_FORMAT_ISO8601); 254 | 255 | $server['HTTP_' . HttpUtils::AUTHORIZATION_HEADER] = 'DTA1-HMAC-SHA256 SignedHeaders=xxx, Credential=KEYID/20110909, Signature=xxx'; 256 | 257 | $content = 'body'; 258 | 259 | $request = new Request($server, $content); 260 | 261 | $credential = new Credential('SECRETKEY', 'KEYID'); 262 | $store = new CredentialStore(); 263 | $store->add($credential); 264 | 265 | $signer = new Signer(); 266 | 267 | $this->assertFalse($signer->verify($request, $store)); 268 | } 269 | 270 | public function testVerifyRequestWithInvalidSignature() 271 | { 272 | $server = array(); 273 | $server['HTTP_HOST'] = 'amazon.com'; 274 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 275 | $server['SERVER_PORT'] = '80'; 276 | $server['REQUEST_URI'] = '/'; 277 | $server['REQUEST_METHOD'] = 'GET'; 278 | $server['CONTENT_TYPE'] = 'application/json'; 279 | 280 | $dateNow = new \DateTime('@' . time()); 281 | $server['HTTP_' . HttpUtils::X_AMZ_DATE_HEADER] = $dateNow->format(DateUtils::DATE_FORMAT_ISO8601); 282 | 283 | $server['HTTP_' . HttpUtils::AUTHORIZATION_HEADER] = 'DTA1-HMAC-SHA256 SignedHeaders=xxx, Credential=KEYID/' . $dateNow->format(DateUtils::DATE_FORMAT_SHORT) . ', Signature=xxx'; 284 | 285 | $content = 'body'; 286 | 287 | $request = new Request($server, $content); 288 | 289 | $credential = new Credential('SECRETKEY', 'KEYID'); 290 | $store = new CredentialStore(); 291 | $store->add($credential); 292 | 293 | $signer = new Signer(); 294 | 295 | $this->assertFalse($signer->verify($request, $store)); 296 | } 297 | 298 | public function testGetAuthorizationHeaderInvalidDates() 299 | { 300 | $this->setExpectedException('InvalidArgumentException'); 301 | 302 | $server = array(); 303 | $server['HTTP_HOST'] = 'amazon.com'; 304 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 305 | $server['SERVER_PORT'] = '80'; 306 | $server['REQUEST_URI'] = '/service/foo.php'; 307 | $server['REQUEST_METHOD'] = 'POST'; 308 | $server['HTTP_ACCEPT'] = '*/*'; 309 | $server['CONTENT_TYPE'] = 'application/json'; 310 | 311 | $content = '{"operation" : "bar"}'; 312 | 313 | $request = new Request($server, $content); 314 | 315 | $signer = new Signer(); 316 | $header = $signer->getAuthorizationHeader($request, new Credential('SECRET', 'PUBLIC'), null, null); 317 | } 318 | } 319 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Utils/HttpUtilsTest.php: -------------------------------------------------------------------------------- 1 | assertNotNull($url); 32 | $this->assertEquals('http://amazon.com/index.html?a=1', $url); 33 | } 34 | 35 | public function testParseFullURLWithCustomPort() 36 | { 37 | $server = array(); 38 | 39 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 40 | $server['SERVER_PORT'] = '1234'; 41 | $server['HTTP_HOST'] = 'amazon.com'; 42 | $server['REQUEST_URI'] = '/index.html?a=1'; 43 | 44 | $url = HttpUtils::parseFullURL($server); 45 | 46 | $this->assertNotNull($url); 47 | $this->assertEquals('http://amazon.com:1234/index.html?a=1', $url); 48 | } 49 | 50 | public function testParseFullURLWithSSL() 51 | { 52 | $server = array(); 53 | 54 | $server['HTTPS'] = 'on'; 55 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 56 | $server['SERVER_PORT'] = '443'; 57 | $server['HTTP_HOST'] = 'amazon.com'; 58 | $server['REQUEST_URI'] = '/index.html?a=1'; 59 | 60 | $url = HttpUtils::parseFullURL($server); 61 | 62 | $this->assertNotNull($url); 63 | $this->assertEquals('https://amazon.com/index.html?a=1', $url); 64 | } 65 | 66 | public function testParseFullURLWithSSLAndCustomPort() 67 | { 68 | $server = array(); 69 | 70 | $server['HTTPS'] = 'on'; 71 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 72 | $server['SERVER_PORT'] = '1234'; 73 | $server['HTTP_HOST'] = 'amazon.com'; 74 | $server['REQUEST_URI'] = '/index.html?a=1'; 75 | 76 | $url = HttpUtils::parseFullURL($server); 77 | 78 | $this->assertNotNull($url); 79 | $this->assertEquals('https://amazon.com:1234/index.html?a=1', $url); 80 | } 81 | 82 | public function testParseFullURLWithForwardedHost() 83 | { 84 | $server = array(); 85 | 86 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 87 | $server['SERVER_PORT'] = '1234'; 88 | $server['HTTP_HOST'] = 'amazon.com'; 89 | $server['HTTP_X_FORWARDED_HOST'] = 'amazon2.com'; 90 | $server['REQUEST_URI'] = '/index.html?a=1'; 91 | 92 | $url = HttpUtils::parseFullURL($server); 93 | 94 | $this->assertNotNull($url); 95 | $this->assertEquals('http://amazon2.com:1234/index.html?a=1', $url); 96 | } 97 | 98 | public function testParseFullURLWithServerName() 99 | { 100 | $server = array(); 101 | 102 | $server['SERVER_PROTOCOL'] = 'HTTP/1.1'; 103 | $server['SERVER_PORT'] = '1234'; 104 | $server['SERVER_NAME'] = 'amazon.com'; 105 | $server['REQUEST_URI'] = '/index.html?a=1'; 106 | 107 | $url = HttpUtils::parseFullURL($server); 108 | 109 | $this->assertNotNull($url); 110 | $this->assertEquals('http://amazon.com:1234/index.html?a=1', $url); 111 | } 112 | 113 | public function testParseRequestHeaders() 114 | { 115 | $server = array(); 116 | 117 | $server['HTTP_HOST'] = 'amazon.com'; 118 | $server['HTTP_ACCEPT'] = 'text/html,application/json;q=1'; 119 | $server['HTTP_CONNECTION'] = 'keep-alive'; 120 | $server['HTTP_ACCEPT_ENCODING'] = 'gzip,deflate,sdch'; 121 | $server['SERVER_PORT'] = '80'; 122 | 123 | $server['CONTENT_TYPE'] = 'application/json'; 124 | $server['CONTENT_LENGTH'] = '32'; 125 | 126 | $headers = HttpUtils::parseRequestHeaders($server); 127 | 128 | $this->assertNotNull($headers); 129 | $this->assertEquals(6, count($headers)); 130 | $this->assertEquals('amazon.com', $headers['host']); 131 | $this->assertEquals('text/html,application/json;q=1', $headers['accept']); 132 | $this->assertEquals('keep-alive', $headers['connection']); 133 | $this->assertEquals('gzip,deflate,sdch', $headers['accept-encoding']); 134 | $this->assertEquals('application/json', $headers['content-type']); 135 | $this->assertEquals('32', $headers['content-length']); 136 | } 137 | 138 | public function testNormalizePath() 139 | { 140 | $this->assertEquals('a/c', HttpUtils::normalizePath('a/c')); 141 | $this->assertEquals('a/c', HttpUtils::normalizePath('a//c')); 142 | $this->assertEquals('a/c', HttpUtils::normalizePath('a/c/.')); 143 | $this->assertEquals('a/c', HttpUtils::normalizePath('a/c/b/..')); 144 | $this->assertEquals('a/c/', HttpUtils::normalizePath('a/c/')); 145 | $this->assertEquals('/a/c', HttpUtils::normalizePath('/../a/c')); 146 | $this->assertEquals('/a/c', HttpUtils::normalizePath('/../a/b/../././/c')); 147 | } 148 | 149 | public function testNormalizeEmptyPath() 150 | { 151 | $this->assertEquals('/', HttpUtils::normalizePath('')); 152 | $this->assertEquals('/', HttpUtils::normalizePath(null)); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /tst/Amazon/InstantAccess/Utils/IOUtilsTest.php: -------------------------------------------------------------------------------- 1 | setExpectedException('InvalidArgumentException'); 23 | 24 | $url = IOUtils::getFilePathFromHandle(null); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tst/bootstrap-phar.php: -------------------------------------------------------------------------------- 1 | pushHandler(new Monolog\Handler\StreamHandler('build/log/test-phar-output.log', Monolog\Logger::DEBUG)); 24 | Amazon\InstantAccess\Log\Logger::setLogger($monolog); 25 | date_default_timezone_set('UTC'); 26 | -------------------------------------------------------------------------------- /tst/bootstrap.php: -------------------------------------------------------------------------------- 1 | pushHandler(new Monolog\Handler\StreamHandler('./build/log/test-output.log', Monolog\Logger::DEBUG)); 24 | Amazon\InstantAccess\Log\Logger::setLogger($monolog); 25 | date_default_timezone_set('UTC'); 26 | --------------------------------------------------------------------------------