Merge pull request #1 from blocktrail/master

Import (Merged) Guzzle4 version of http-signatures from blocktrail:guzzle4
This commit is contained in:
John Barton 2014-10-07 09:40:43 +11:00
commit 55d7e74e99
11 changed files with 348 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/composer.lock
/vendor/

11
.travis.yml Normal file
View File

@ -0,0 +1,11 @@
language: php
php:
- 5.6
- 5.5
- 5.4
- hhvm
install: composer install
script: vendor/bin/phpunit

22
LICENSE.txt Normal file
View File

@ -0,0 +1,22 @@
Copyright (c) 2014 99designs
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -2,3 +2,37 @@ HTTP Signatures Guzzle 4
========================
Guzzle 4 support for 99designs http-signatures library
[![Build Status](https://travis-ci.org/99designs/http-signatures-guzzlehttp.svg)](https://travis-ci.org/99designs/http-signatures-guzzlehttp)
Adds [99designs/http-signatures](http-signatures) support to Guzzle 4.
For Guzzle 3 see the [99designs/http-signatures-guzzle](99designs/http-signatures-guzzle) repo.
Signing with Guzzle 4
---------------------
This library includes support for automatically signing Guzzle requests using an event subscriber.
```php
use HttpSignatures\Context;
use HttpSignatures\GuzzleHttp\RequestSubscriber;
$context = new Context(array(
'keys' => array('examplekey' => 'secret-key-here'),
'algorithm' => 'hmac-sha256',
'headers' => array('(request-target)', 'Date', 'Accept'),
));
$client = new \Guzzle\Http\Client('http://example.org');
$client->getEmiter()->attach(new RequestSubscriber($context));
// The below will now send a signed request to: http://example.org/path?query=123
$client->get('/path?query=123', array(
'Date' => 'Wed, 30 Jul 2014 16:40:19 -0700',
'Accept' => 'llamas',
));
```
## Contributing
Pull Requests are welcome.

30
composer.json Normal file
View File

@ -0,0 +1,30 @@
{
"name": "99designs/http-signatures-guzzlehttp",
"description": "Sign and verify HTTP messages with Guzzle 4",
"homepage": "https://github.com/99designs/http-signatures-guzzlehttp",
"keywords": ["http", "https", "signing", "signed", "signature", "hmac", "guzzle 4"],
"license": "MIT",
"authors": [
{
"name": "Adrian Palmer",
"email": "adrian.palmer@99designs.com"
},
{
"name": "Ruben de Vries",
"email": "ruben@blocktrail.com"
}
],
"autoload": {
"psr-4": {
"": "src/"
}
},
"require": {
"php": ">=5.4.0",
"99designs/http-signatures": "~1.1",
"guzzlehttp/guzzle": "~4.2"
},
"require-dev": {
"phpunit/phpunit": "~4.1"
}
}

13
phpunit.xml.dist Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./tests/bootstrap.php" colors="true">
<testsuites>
<testsuite>
<directory>tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src</directory>
</whitelist>
</filter>
</phpunit>

View File

@ -0,0 +1,47 @@
<?php
namespace HttpSignatures\GuzzleHttp;
use GuzzleHttp\Message\RequestInterface;
/**
* Class RequestMessage
*
* wrapper around the Guzzle Request instance to have a consistent API for the HttpSignatures classes to consume
*
* @package HttpSignatures\Guzzle
*/
class Message
{
/**
* @var RequestInterface
*/
private $request;
/**
* @var MessageHeaders
*/
public $headers;
public function __construct(RequestInterface $request)
{
$this->request = $request;
$this->headers = new MessageHeaders($request);
}
public function getQueryString()
{
$qs = $this->request->getQuery();
return $qs->count() ? $qs : null;
}
public function getMethod()
{
return $this->request->getMethod();
}
public function getPathInfo()
{
return $this->request->getPath();
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace HttpSignatures\GuzzleHttp;
use GuzzleHttp\Message\MessageInterface;
/**
* Class MessageHeaders
*
* wrapper around the Guzzle Request instance to have a consistent API for the HttpSignatures classes to consume
*
* @package HttpSignatures\Guzzle
*/
class MessageHeaders
{
/**
* @var MessageInterface
*/
private $request;
public function __construct(MessageInterface $request)
{
$this->request = $request;
}
public function has($header)
{
return $this->request->hasHeader($header);
}
public function get($header)
{
return $this->request->getHeader($header);
}
public function set($header, $value)
{
$this->request->setHeader($header, $value);
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace HttpSignatures\GuzzleHttp;
use GuzzleHttp\Event\BeforeEvent;
use GuzzleHttp\Event\RequestEvents;
use GuzzleHttp\Event\SubscriberInterface;
use HttpSignatures\Context;
class RequestSubscriber implements SubscriberInterface
{
/**
* @var \HttpSignatures\Context
*/
private $context;
public function __construct(Context $context)
{
$this->context = $context;
}
public function getEvents()
{
return ['before' => ['onBefore', RequestEvents::SIGN_REQUEST]];
}
public function onBefore(BeforeEvent $event)
{
$request = $event->getRequest();
if ($request->getConfig()['auth'] != 'http-signatures') {
return;
}
$this->context->signer()->sign(new Message($request));
}
}

View File

@ -0,0 +1,109 @@
<?php
namespace HttpSignatures\Test;
use HttpSignatures\GuzzleHttp\Message;
use HttpSignatures\GuzzleHttp\RequestSubscriber;
use HttpSignatures\Context;
class GuzzleHttpSignerTest extends \PHPUnit_Framework_TestCase
{
/**
* @var Context
*/
private $context;
/**
* @var \GuzzleHttp\Client
*/
private $client;
public function setUp()
{
$this->context = new Context(array(
'keys' => array('pda' => 'secret'),
'algorithm' => 'hmac-sha256',
'headers' => array('(request-target)', 'date'),
));
$this->client = new \GuzzleHttp\Client([
'auth' => 'http-signatures'
]);
$this->client->getEmitter()->attach(new RequestSubscriber($this->context));
}
/**
* test signing a message
*/
public function testGuzzleRequestHasExpectedHeaders()
{
$message = $this->client->createRequest('GET', '/path?query=123', array(
'headers' => array('date' => 'today', 'accept' => 'llamas')
));
$this->context->signer()->sign(new Message($message));
$expectedString = implode(
',',
array(
'keyId="pda"',
'algorithm="hmac-sha256"',
'headers="(request-target) date"',
'signature="SFlytCGpsqb/9qYaKCQklGDvwgmrwfIERFnwt+yqPJw="',
)
);
$this->assertEquals(
$expectedString,
(string) $message->getHeader('Signature')
);
$this->assertEquals(
'Signature ' . $expectedString,
(string) $message->getHeader('Authorization')
);
}
/**
* test signing a message with a URL that doesn't contain a ?query
*/
public function testGuzzleRequestHasExpectedHeaders2()
{
$message = $this->client->createRequest('GET', '/path', array(
'headers' => array('date' => 'today', 'accept' => 'llamas')
));
$this->context->signer()->sign(new Message($message));
$expectedString = implode(
',',
array(
'keyId="pda"',
'algorithm="hmac-sha256"',
'headers="(request-target) date"',
'signature="DAtF133khP05pS5Gh8f+zF/UF7mVUojMj7iJZO3Xk4o="',
)
);
$this->assertEquals(
$expectedString,
(string) $message->getHeader('Signature')
);
$this->assertEquals(
'Signature ' . $expectedString,
(string) $message->getHeader('Authorization')
);
}
public function testVerifyGuzzleRequest()
{
$message = $this->client->createRequest('GET', '/path?query=123', array(
'headers' => array('date' => 'today', 'accept' => 'dogs')
));
$this->context->signer()->sign(new Message($message));
$this->assertTrue($this->context->verifier()->isValid(new Message($message)));
}
}

3
tests/bootstrap.php Normal file
View File

@ -0,0 +1,3 @@
<?php
error_reporting(E_ALL);
require(__DIR__ . '/../vendor/autoload.php');