Skip to content

Commit

Permalink
Add tests for Authorization, fighting with curlwrappers now.
Browse files Browse the repository at this point in the history
  • Loading branch information
beberlei committed Mar 26, 2012
1 parent 9de0dff commit 54d91b8
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 23 deletions.
22 changes: 12 additions & 10 deletions lib/Doctrine/KeyValueStore/Http/StreamClient.php
Expand Up @@ -56,33 +56,35 @@ class StreamClient implements Client
public function request($method, $url, $body = null, array $headers)
{
$parts = parse_url($url);
$host = $parts['url'];
$host = $parts['host'];

$header = "";
$header2 = array();
foreach ($headers as $headerName => $v) {
foreach ((array)$v as $value) {
$header .= $headerName . ": " . $value . "\r\n";
}
}
$header = rtrim($header);

// TODO SSL support?
$httpFilePointer = @fopen(
$url,
'r',
false,
stream_context_create(
array(
$opts = array(
'http' => array(
'method' => $method,
'content' => $data,
'content' => $body,
'ignore_errors' => true,
'max_redirects' => 0,
'user_agent' => 'Doctrine KeyValueStore',
'timeout' => $this->options['timeout'],
'header' => $header,
),
)
)
);

$httpFilePointer = fopen(
$url,
'r',
false,
stream_context_create($opts)
);

// Check if connection has been established successfully
Expand Down
Expand Up @@ -33,7 +33,7 @@ class SharedKeyAuthorization implements AuthorizationSchema
public function __construct($accountName, $accountKey)
{
$this->accountName = $accountName;
$this->accountKey = $accountKey;
$this->accountKey = base64_decode($accountKey);
}

/**
Expand All @@ -42,13 +42,13 @@ public function __construct($accountName, $accountKey)
*/
public function signRequest($method, $path, $queryString, $body, array $headers)
{
$canonicalResource = "/" . $this->accountName . '/' . $path;
$canonicalResource = "/" . $this->accountName . $path;
$stringToSign = $method . "\n" .
md5($body) . "\n" .
"application/atom+xml\n" .
$headers['x-ms-date'] . "\n" .
$canonicalResource;
return "Authorization: SharedKey " . base64_encode(hash_hmac('sha256', $stringToSign, $this->accountKey, true));
return "Authorization: SharedKey " . $this->accountName . ":" . base64_encode(hash_hmac('sha256', $stringToSign, $this->accountKey, true));
}
}

@@ -0,0 +1,51 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/

namespace Doctrine\KeyValueStore\Storage\WindowsAzureTable;

class SharedKeyLiteAuthorization implements AuthorizationSchema
{
/**
* @var string
*/
private $accountName;
/**
* @var string
*/
private $accountKey;

public function __construct($accountName, $accountKey)
{
$this->accountName = $accountName;
$this->accountKey = base64_decode($accountKey);
}

/**
* @override
* {@inheritDocs}
*/
public function signRequest($method, $path, $queryString, $body, array $headers)
{
$canonicalResource = "/" . $this->accountName . $path;
$stringToSign = $headers['x-ms-date'] . "\n" .
$canonicalResource;
return "Authorization: SharedKeyLite " . $this->accountName . ":" . base64_encode(hash_hmac('sha256', $stringToSign, $this->accountKey, true));
}
}

58 changes: 51 additions & 7 deletions lib/Doctrine/KeyValueStore/Storage/WindowsAzureTableStorage.php
Expand Up @@ -31,7 +31,7 @@
*/
class WindowsAzureTableStorage implements Storage
{
const WINDOWS_AZURE_TABLE_BASEURL = 'https://%s.table.core.windows.net';
const WINDOWS_AZURE_TABLE_BASEURL = 'http://%s.table.core.windows.net';

const METADATA_NS = 'http://schemas.microsoft.com/ado/2007/08/dataservices/metadata';
const DATA_NS = 'http://schemas.microsoft.com/ado/2007/08/dataservices';
Expand All @@ -53,6 +53,21 @@ class WindowsAzureTableStorage implements Storage
</content>
</entry>';

const XML_TEMPLATE_TABLE = '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<entry xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
<title />
<updated></updated>
<author>
<name/>
</author>
<id/>
<content type="application/xml">
<m:properties>
<d:TableName />
</m:properties>
</content>
</entry>';

const TYPE_INT32 = 'Edm.Int32';
const TYPE_INT64 = 'Edm.Int64';
const TYPE_DATETIME = 'Edm.DateTime';
Expand Down Expand Up @@ -118,10 +133,10 @@ private function isoDate(\DateTime $date)
return str_replace('+00:00', '.0000000Z', $date->format('c'));
}

private function createDomDocumentRequestBody()
private function createDomDocumentRequestBody($xml = null)
{
$dom = new \DOMDocument('1.0', 'UTF-8');
$dom->loadXML(self::XML_TEMPLATE_ENTITY);
$dom->loadXML($xml ?: self::XML_TEMPLATE_ENTITY);

$updatedNodes = $dom->getElementsByTagName('updated');
$updatedNodes->item(0)->appendChild($dom->createTextNode($this->now()));
Expand All @@ -133,7 +148,6 @@ public function insert($storageName, $key, array $data)
{
$headers = array(
'Content-Type' => 'application/atom+xml',
'x-ms-date' => $this->now(),
);
// TODO: This sucks
$tableName = $storageName;
Expand All @@ -150,7 +164,28 @@ public function insert($storageName, $key, array $data)
$xml = $dom->saveXML();

$url = $this->baseUrl . '/' . $tableName;
$this->request('POST', $url, $xml, $headers);
$response = $this->request('POST', $url, $xml, $headers);

if ($response->getStatusCode() == 404) {
$this->createTable($tableName);
$this->insert($storageName, $key, $data);
}
}

public function createTable($tableName)
{
$headers = array(
'Content-Type' => 'application/atom+xml',
'x-ms-version' => '2009-09-19',
);

$dom = $this->createDomDocumentRequestBody(self::XML_TEMPLATE_TABLE);
$tableNode = $dom->getElementsByTagNameNS(self::DATA_NS, 'TableName')->item(0);
$tableNode->appendChild($dom->createTextNode($tableName));
$xml = $dom->saveXML();

$url = $this->baseUrl . '/Tables';
$response = $this->request('POST', $url, $xml, $headers);
}

private function serializeKeys($propertiesNode, $key)
Expand All @@ -176,9 +211,18 @@ private function serializeKeys($propertiesNode, $key)
private function request($method, $url, $xml, $headers)
{
$parts = parse_url($url);
$authorizationHeader = $this->authorization->signRequest($method, $xml, $parts['path'], isset($parts['query']) ? $parts['query'] : '', $headers);
$authorizationParts = explode(":" , $authorizationHeader, 2);
$requestDate = $this->now->format('D, d M Y H:i:s') . ' GMT';
$headers['Content-Length'] = strlen($xml);
$headers['Date'] = $requestDate;
$headers['x-ms-date'] = $requestDate;
$authorizationHeader = $this->authorization->signRequest(
$method,
isset($parts['path']) ? $parts['path'] : '/',
isset($parts['query']) ? $parts['query'] : '',
$xml,
$headers
);
$authorizationParts = explode(":" , $authorizationHeader, 2);
$headers[$authorizationParts[0]] = ltrim($authorizationParts[1]);
return $this->client->request($method, $url, $xml, $headers);
}
Expand Down
Expand Up @@ -3,7 +3,7 @@

use Doctrine\Tests\KeyValueStoreTestCase;
use Doctrine\KeyValueStore\Storage\WindowsAzureTableStorage;
use Doctrine\KeyValueStore\Storage\WindowsAzureTable\SharedKeyAuthorization;
use Doctrine\KeyValueStore\Storage\WindowsAzureTable\SharedKeyLiteAuthorization;
use Doctrine\KeyValueStore\Http\StreamClient;

class WindowsAzureTableTest extends KeyValueStoreTestCase
Expand All @@ -16,7 +16,7 @@ public function testCrud()

switch ($GLOBALS['DOCTRINE_KEYVALUE_AZURE_AUTHSCHEMA']) {
case 'shared':
$auth = new SharedKeyAuthorization(
$auth = new SharedKeyLiteAuthorization(
$GLOBALS['DOCTRINE_KEYVALUE_AZURE_NAME'],
$GLOBALS['DOCTRINE_KEYVALUE_AZURE_KEY']
);
Expand All @@ -29,7 +29,7 @@ public function testCrud()
$auth
);

$storage->insert("test", array("partition" => "foo", "range" => 100), array("foo" => "bar"));
$storage->createTable("test");
}
}

@@ -0,0 +1,41 @@
<?php

namespace Doctrine\Tests\KeyValueStore\Storage\WindowsAzureTable;

use Doctrine\KeyValueStore\Storage\WindowsAzureTable\SharedKeyLiteAuthorization;

class SharedKeyLiteTest extends \PHPUnit_Framework_TestCase
{
private $auth;

public function setUp()
{
$this->auth = new SharedKeyLiteAuthorization(
"testing",
"abcdefg"
);
}

public function testKeyGeneration1()
{
$authorization = $this->auth->signRequest('GET', '/', '', '', array(
"x-ms-date" => "Wed, 29 Apr 2009 13:12:47 GMT"
));
$this->assertEquals(
"Authorization: SharedKeyLite testing:vZdOn/j0gW5FG0kAUG9NhSBO9eBjZqfe6RwALPYUtqU=",
$authorization
);
}

public function testKeyGeneration2()
{
$authorization = $this->auth->signRequest('GET', '/test', '', '', array(
"x-ms-date" => "Wed, 29 Apr 2009 13:12:47 GMT"
));
$this->assertEquals(
"Authorization: SharedKeyLite testing:HJTSiRDtMsQVsFVispSHkcODeFykLO+WEuOepwmh51o=",
$authorization
);
}
}

0 comments on commit 54d91b8

Please sign in to comment.