Guzzle4 support

This commit is contained in:
Ruben de Vries 2014-10-06 10:21:26 +02:00
parent 78cccf1a8f
commit a20de9b9bd
10 changed files with 225 additions and 129 deletions

View File

@ -4,7 +4,6 @@ php:
- 5.6 - 5.6
- 5.5 - 5.5
- 5.4 - 5.4
- 5.3
- hhvm - hhvm
install: composer install install: composer install

View File

@ -1,18 +1,19 @@
HTTP Signatures Guzzle 3 HTTP Signatures Guzzle 4
=== ===
[![Build Status](https://travis-ci.org/99designs/http-signatures-guzzle.svg)](https://travis-ci.org/99designs/http-signatures-guzzle) [![Build Status](https://travis-ci.org/99designs/http-signatures-guzzlehttp.svg)](https://travis-ci.org/99designs/http-signatures-guzzlehttp)
Adds Guzzle 3 support to [99designs/http-signatures][99signatures] 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 3 Signing with Guzzle 4
--- ---
This library includes support for automatically signing Guzzle requests using an event subscriber. This library includes support for automatically signing Guzzle requests using an event subscriber.
```php ```php
use HttpSignatures\Context; use HttpSignatures\Context;
use HttpSignatures\Guzzle\RequestSubscriber; use HttpSignatures\GuzzleHttp\RequestSubscriber;
$context = new Context(array( $context = new Context(array(
'keys' => array('examplekey' => 'secret-key-here'), 'keys' => array('examplekey' => 'secret-key-here'),
@ -21,17 +22,15 @@ $context = new Context(array(
)); ));
$client = new \Guzzle\Http\Client('http://example.org'); $client = new \Guzzle\Http\Client('http://example.org');
$client->addSubscriber(new RequestSubscriber($context)); $client->getEmiter()->attach(new RequestSubscriber($context));
// The below will now send a signed request to: http://example.org/path?query=123 // The below will now send a signed request to: http://example.org/path?query=123
$client->get('/path?query=123', array( $client->get('/path?query=123', array(
'Date' => 'Wed, 30 Jul 2014 16:40:19 -0700', 'Date' => 'Wed, 30 Jul 2014 16:40:19 -0700',
'Accept' => 'llamas', 'Accept' => 'llamas',
))->send(); ));
``` ```
## Contributing ## Contributing
Pull Requests are welcome. Pull Requests are welcome.
[99signatures]: https://github.com/99designs/http-signatures-php

View File

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

View File

@ -1,30 +0,0 @@
<?php
namespace HttpSignatures\Guzzle;
class Message
{
private $request;
public $headers;
public function __construct($request)
{
$this->request = $request;
$this->headers = new MessageHeaders($request);
}
public function getQueryString()
{
return $this->request->getQuery(true);
}
public function getMethod()
{
return $this->request->getMethod();
}
public function getPathInfo()
{
return $this->request->getPath();
}
}

View File

@ -1,28 +0,0 @@
<?php
namespace HttpSignatures\Guzzle;
use Guzzle\Common\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class RequestSubscriber implements EventSubscriberInterface
{
private $context;
function __construct($context)
{
$this->context = $context;
}
public static function getSubscribedEvents()
{
return array(
'client.create_request' => 'signRequest'
);
}
public function signRequest($e)
{
$this->context->signer()->sign(new Message($e['request']));
}
}

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

@ -1,12 +1,24 @@
<?php <?php
namespace HttpSignatures\Guzzle; 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 class MessageHeaders
{ {
/**
* @var MessageInterface
*/
private $request; private $request;
public function __construct($request) public function __construct(MessageInterface $request)
{ {
$this->request = $request; $this->request = $request;
} }

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)));
}
}

View File

@ -1,53 +0,0 @@
<?php
namespace HttpSignatures\Test;
use HttpSignatures\Guzzle\RequestSubscriber;
use HttpSignatures\Context;
use HttpSignatures\Guzzle\Message;
class GuzzleSignerTest extends \PHPUnit_Framework_TestCase
{
public function setUp()
{
$this->context = new Context(array(
'keys' => array('pda' => 'secret'),
'algorithm' => 'hmac-sha256',
'headers' => array('(request-target)', 'date'),
));
$this->client = new \Guzzle\Http\Client();
$this->client->addSubscriber(new RequestSubscriber($this->context));
}
public function testGuzzleRequestHasExpectedHeaders()
{
$message = $this->client->get('/path?query=123', array('date' => 'today', 'accept' => 'llamas'));
$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')
);
}
public function testVerifyGuzzleRequest()
{
$message = $this->client->get('/path?query=123', array('date' => 'today', 'accept' => 'dogs'));
$this->assertTrue($this->context->verifier()->isValid(new Message($message)));
}
}