Fixing merge

This commit is contained in:
Matt Farina 2012-08-06 13:21:48 -04:00
commit 83604c466b
22 changed files with 735 additions and 67 deletions

View File

@ -3,7 +3,7 @@
"description": "Access HPCloud and OpenStack services in PHP.",
"type": "library",
"keywords": ["openstack","hpcloud","cloud","swift","nova"],
"license": "MIT-like",
"license": "MIT",
"homepage": "http://hpcloud.com",
"authors": [
{

View File

@ -171,12 +171,16 @@
*
* @code
* <?php
* // The explicit way:
* // Find out where our ObjectStorage instance lives:
* $storageList = $identity->serviceCatalog('object-storage');
* $objectStorageUrl = storageList[0]['endpoints'][0]['publicURL'];
* // $storageList = $identity->serviceCatalog('object-storage');
* // $objectStorageUrl = storageList[0]['endpoints'][0]['publicURL'];
*
* // Create a new ObjectStorage instance:
* $objectStore = new \HPCloud\Storage\ObjectStorage($token, $objectStorageUrl);
* // $objectStore = new \HPCloud\Storage\ObjectStorage($token, $objectStorageUrl);
*
* // Or let ObjectStorage figure out which instance to use:
* $objectStore = \HPCloud\Storage\ObjectStorage::newFromIdentity($identity);
*
* // List containers:
* print_r($objectStore->containers());

View File

@ -269,6 +269,9 @@ Now we can get a new HPCloud::Storage::ObjectStorage instance:
$catalog = $idService->serviceCatalog();
$store = ObjectStorage::newFromServiceCatalog($catalog, $token);
// UPDATE: As of Beta 6, you can use newFromIdentity():
// $store = ObjectStorage::newFromIdentity($idService);
?>
~~~

View File

@ -29,6 +29,9 @@ SOFTWARE.
namespace HPCloud;
use HPCloud\Services\IdentityServices;
use HPCloud\Exception;
/**
* Bootstrapping services.
*
@ -128,6 +131,12 @@ class Bootstrap {
'transport' => '\HPCloud\Transport\CURLTransport',
);
/**
* An identity services object created from the global settings.
* @var object HPCloud::Services::IdentityServices
*/
public static $identity = NULL;
/**
* Add the autoloader to PHP's autoloader list.
*
@ -313,4 +322,56 @@ class Bootstrap {
public static function hasConfig($name) {
return isset(self::$config[$name]);
}
/**
* Get a HPCloud::Services::IdentityService object from the bootstrap config.
*
* A factory helper function that uses the bootstrap configuration to create
* a ready to use HPCloud::Services::IdentityService object.
*
* @param bool $force
* Whether to force the generation of a new object even if one is already
* cached.
* @retval HPCloud::Services::IdentityService
* An authenticated ready to use HPCloud::Services::IdentityService object.
* @throws HPCloud::Exception
* When the needed configuration to authenticate is not available.
*/
public static function identity($force = FALSE) {
// If we already have an identity make sure the token is not expired.
if ($force || is_null(self::$identity) || self::$identity->isExpired()) {
// Make sure we have an endpoint to use
if (!self::hasConfig('endpoint')) {
throw new Exception('Unable to authenticate. No endpoint supplied.');
}
// Neither user nor account can be an empty string, so we need
// to do more checking than self::hasConfig(), which returns TRUE
// if an item exists and is an empty string.
$user = self::config('username', NULL);
$account = self::config('account', NULL);
// Check if we have a username/password
if (!empty($user) && self::hasConfig('password')) {
$is = new IdentityServices(self::config('endpoint'));
$is->authenticateAsUser($user, self::config('password'), self::config('tenantid', NULL), self::config('tenantname', NULL));
self::$identity = $is;
}
// Otherwise we go with access/secret keys
elseif (!empty($account) && self::hasConfig('secret')) {
$is = new IdentityServices(self::config('endpoint'));
$is->authenticateAsAccount($account, self::config('secret'), self::config('tenantid', NULL), self::config('tenantname', NULL));
self::$identity = $is;
}
else {
throw new Exception('Unable to authenticate. No account credentials supplied.');
}
}
return self::$identity;
}
}

View File

@ -122,9 +122,14 @@ namespace HPCloud\Services;
* - tenants()
* - rescope()
*
* <b>Serializing</b>
*
* IdentityServices has been intentionally built to serialize well.
* This allows implementors to cache IdentityServices objects rather
* than make repeated requests for identity information.
*
*/
class IdentityServices {
class IdentityServices /*implements Serializable*/ {
/**
* The version of the API currently supported.
*/
@ -171,6 +176,8 @@ class IdentityServices {
*/
protected $catalog = array();
protected $userDetails;
/**
* Build a new IdentityServices object.
*
@ -266,7 +273,6 @@ class IdentityServices {
'auth' => $ops,
);
$body = json_encode($envelope);
$headers = array(
@ -288,17 +294,20 @@ class IdentityServices {
}
/**
* Authenticate to Identity Services with username, password, and tenant ID.
* Authenticate to Identity Services with username, password, and either
* tenant ID or tenant Name.
*
* Given an HPCloud username and password, authenticate to Identity Services.
* Identity Services will then issue a token that can be used to access other
* HPCloud services.
*
* If a tenant ID is provided, this will also associate the user with the
* given tenant ID.
* given tenant ID. If a tenant Name is provided, this will associate the user
* with the given tenant Name. Only the tenant ID or tenant Name needs to be
* given, not both.
*
* If no tenant ID is given, it will likely be necessary to rescope() the
* request (See also tenants()).
* If no tenant ID or tenant Name is given, it will likely be necessary to
* rescope() the request (See also tenants()).
*
* Other authentication methods:
*
@ -312,13 +321,16 @@ class IdentityServices {
* @param string $tenantId
* The tenant ID for this account. This can be obtained through the
* HPCloud console.
* @param string $tenantName
* The tenant Name for this account. This can be obtained through the
* HPCloud console.
* @throws HPCloud::Transport::AuthorizationException
* If authentication failed.
* @throws HPCloud::Exception
* For abnormal network conditions. The message will give an indication as
* to the underlying problem.
*/
public function authenticateAsUser($username, $password, $tenantId = NULL) {
public function authenticateAsUser($username, $password, $tenantId = NULL, $tenantName = NULL) {
$ops = array(
'passwordCredentials' => array(
'username' => $username,
@ -330,6 +342,12 @@ class IdentityServices {
if (!empty($tenantId)) {
$ops['tenantId'] = $tenantId;
}
// If a tenant name is provided, add it to the auth array.
if (!empty($tenantName)) {
$ops['tenantName'] = $tenantName;
}
return $this->authenticate($ops);
}
/**
@ -342,10 +360,11 @@ class IdentityServices {
* The account ID and access key information can be found in the account
* section of the console.
*
* The third paramater allows you to specify a tenant ID. In order to access
* services, this object will need a tenant ID. If none is specified, it can
* be set later using rescope(). The tenants() method can be used to get a
* list of all available tenant IDs for this token.
* The third and fourth paramaters allow you to specify a tenant ID or
* tenantName. In order to access services, this object will need a tenant ID
* or tenant name. If none is specified, it can be set later using rescope().
* The tenants() method can be used to get a list of all available tenant IDs
* for this token.
*
* Other authentication methods:
*
@ -361,6 +380,9 @@ class IdentityServices {
* @param string $tenantId
* A valid tenant ID. This will be used to associate a tenant's services
* with this token.
* @param string $tenantName
* The tenant Name for this account. This can be obtained through the
* HPCloud console.
* @retval string
* The auth token.
* @throws HPCloud::Transport::AuthorizationException
@ -369,7 +391,7 @@ class IdentityServices {
* For abnormal network conditions. The message will give an indication as
* to the underlying problem.
*/
public function authenticateAsAccount($account, $key, $tenantId = NULL) {
public function authenticateAsAccount($account, $key, $tenantId = NULL, $tenantName = NULL) {
$ops = array(
'apiAccessKeyCredentials' => array(
'accessKey' => $account,
@ -380,6 +402,10 @@ class IdentityServices {
if (!empty($tenantId)) {
$ops['tenantId'] = $tenantId;
}
if (!empty($tenantName)) {
$ops['tenantName'] = $tenantName;
}
return $this->authenticate($ops);
}
@ -416,11 +442,16 @@ class IdentityServices {
}
/**
* Get the tenant name.
* Get the tenant name associated with this token.
*
* If this token has a tenant name, the name will be returned. Otherwise, this
* will return NULL.
*
* This will not be populated until after an authentication method has been
* run.
*
* @retval string
* The tenant name. Often this is an email
* address or other alpha-numeric string.
* The tenant name if available, or NULL.
*/
public function tenantName() {
if (!empty($this->tokenDetails['tenant']['name'])) {
@ -460,6 +491,31 @@ class IdentityServices {
return $this->tokenDetails;
}
/**
* Check whether the current identity has an expired token.
*
* This does not perform a round-trip to the server. Instead, it compares the
* machine's local timestamp with the server's expiration time stamp. A
* mis-configured machine timestamp could give spurious results.
*
* @retval boolean
* This will return FALSE if there is a current token and it has
* not yet expired (according to the date info). In all other cases
* it returns TRUE.
*/
public function isExpired() {
$details = $this->tokenDetails();
if (empty($details['expires'])) {
return TRUE;
}
$currentDateTime = new \DateTime('now');
$expireDateTime = new \DateTime($details['expires']);
return $currentDateTime > $expireDateTime;
}
/**
* Get the service catalog, optionaly filtering by type.
*
@ -638,6 +694,14 @@ class IdentityServices {
}
/**
* @see HPCloud::Services::IdentityServices::rescopeUsingTenantId()
* @deprecated
*/
public function rescope($tenantId) {
return $this->rescopeUsingTenantId($tenantId);
}
/**
* Rescope the authentication token to a different tenant.
*
* Note that this will rebuild the service catalog and user information for
@ -667,7 +731,7 @@ class IdentityServices {
* For abnormal network conditions. The message will give an indication as
* to the underlying problem.
*/
public function rescope($tenantId) {
public function rescopeUsingTenantId($tenantId) {
$url = $this->url() . '/tokens';
$token = $this->token();
$data = array(
@ -694,6 +758,63 @@ class IdentityServices {
return $this->token();
}
/**
* Rescope the authentication token to a different tenant.
*
* Note that this will rebuild the service catalog and user information for
* the current object, since this information is sensitive to tenant info.
*
* An authentication token can be in one of two states:
*
* - unscoped: It has no associated tenant ID.
* - scoped: It has a tenant ID, and can thus access that tenant's services.
*
* This method allows you to do any of the following:
*
* - Begin with an unscoped token, and assign it a tenant ID.
* - Change a token from one tenant ID to another (re-scoping).
* - Remove the tenant ID from a scoped token (unscoping).
*
* @param string $tenantName
* The tenant name that this present token should be bound to. If this is the
* empty string (`''`), the present token will be "unscoped" and its tenant
* name will be removed.
*
* @retval string
* The authentication token.
* @throws HPCloud::Transport::AuthorizationException
* If authentication failed.
* @throws HPCloud::Exception
* For abnormal network conditions. The message will give an indication as
* to the underlying problem.
*/
public function rescopeUsingTenantName($tenantName) {
$url = $this->url() . '/tokens';
$token = $this->token();
$data = array(
'auth' => array(
'tenantName' => $tenantName,
'token' => array(
'id' => $token,
),
),
);
$body = json_encode($data);
$headers = array(
'Accept' => self::ACCEPT_TYPE,
'Content-Type' => 'application/json',
'Content-Length' => strlen($body),
//'X-Auth-Token' => $token,
);
$client = \HPCloud\Transport::instance();
$response = $client->doRequest($url, 'POST', $headers, $body);
$this->handleResponse($response);
return $this->token();
}
/**
* Given a response object, populate this object.
*
@ -712,4 +833,24 @@ class IdentityServices {
$this->serviceCatalog = $json['access']['serviceCatalog'];
}
/* Not necessary.
public function serialize() {
$data = array(
'tokenDetails' => $this->tokenDetails,
'userDetails' => $this->userDetails,
'serviceCatalog' => $this->serviceCatalog,
'endpoint' => $this->endpoint,
);
return serialize($data);
}
public function unserialize($data) {
$vals = unserialize($data);
$this->tokenDetails = $vals['tokenDetails'];
$this->userDetails = $vals['userDetails'];
$this->serviceCatalog = $vals['serviceCatalog'];
$this->endpoint = $vals['endpoint'];
}
*/
}

View File

@ -138,6 +138,28 @@ class CDN {
*/
protected $token;
/**
* Create a new instance from an IdentityServices object.
*
* This builds a new CDN instance form an authenticated
* IdentityServices object.
*
* In the service catalog, this selects the first service entry
* for CDN. At this time, that is sufficient.
*
* @param HPCloud::Services::IdentityServices $identity
* The identity to use.
* @retval object
* A CDN object or FALSE if no CDN services could be found
* in the catalog.
*/
public static function newFromIdentity($identity) {
$tok = $identity->token();
$cat = $identity->serviceCatalog();
return self::newFromServiceCatalog($cat, $tok);
}
/**
* Create a new CDN object based on a service catalog.
*

View File

@ -163,6 +163,24 @@ class ObjectStorage {
return $store;
}
/**
* Given an IdentityServices instance, create an ObjectStorage instance.
*
* This constructs a new ObjectStorage from an authenticated instance
* of an HPCloud::Services::IdentityServices object.
*
* @param HPCloud::Services::IdentityServices $identity
* An identity services object that already has a valid token and a
* service catalog.
* @retval object ObjectStorage
* A new ObjectStorage instance.
*/
public static function newFromIdentity($identity) {
$cat = $identity->serviceCatalog();
$tok = $identity->token();
return self::newFromServiceCatalog($cat, $tok);
}
/**
* Given a service catalog and an token, create an ObjectStorage instance.
*

View File

@ -148,7 +148,7 @@ class Container implements \Countable, \IteratorAggregate {
*/
public static function objectUrl($base, $oname) {
if (strpos($oname, '/') === FALSE) {
return $base . '/' . $oname;
return $base . '/' . rawurlencode($oname);
}
$oParts = explode('/', $oname);
@ -513,7 +513,7 @@ class Container implements \Countable, \IteratorAggregate {
if (empty($file)) {
// Now build up the rest of the headers:
$headers['ETag'] = $obj->eTag();
$headers['Etag'] = $obj->eTag();
// If chunked, we set transfer encoding; else
// we set the content length.
@ -541,7 +541,7 @@ class Container implements \Countable, \IteratorAggregate {
$hash = hash_init('md5');
hash_update_stream($hash, $file);
$etag = hash_final($hash);
$headers['ETag'] = $etag;
$headers['Etag'] = $etag;
// Not sure if this is necessary:
rewind($file);

View File

@ -451,6 +451,28 @@ class Object {
return $this->additionalHeaders;
}
/**
* Remove headers.
*
* This takes an array of header names, and removes
* any matching headers. Typically, only headers set
* by setAdditionalHeaders() are removed from an Object.
* (RemoteObject works differently).
*
* @attention
* Many headers are generated automatically, such as
* Content-Type and Content-Length. Removing these
* will simply result in their being regenerated.
*
* @param array $keys
* The header names to be removed.
*/
public function removeHeaders($keys) {
foreach ($keys as $k) {
unset($this->additionalHeaders[$k]);
}
}
/**
* This object should be transmitted in chunks.
*

View File

@ -65,7 +65,7 @@ class RemoteObject extends Object {
* serve as a good indicator that the object does not have all
* attributes set.
*/
protected $allHeaders;
protected $allHeaders = array();
protected $cdnUrl;
protected $cdnSslUrl;
@ -125,7 +125,8 @@ class RemoteObject extends Object {
public static function newFromHeaders($name, $headers, $token, $url, $cdnUrl = NULL, $cdnSslUrl = NULL) {
$object = new RemoteObject($name);
$object->allHeaders = $headers;
//$object->allHeaders = $headers;
$object->setHeaders($headers);
//throw new \Exception(print_r($headers, TRUE));
@ -259,6 +260,16 @@ class RemoteObject extends Object {
return $this->metadata;
}
public function setHeaders($headers) {
$this->allHeaders = array();
foreach ($headers as $name => $value) {
if (strpos($name, Container::METADATA_HEADER_PREFIX) !== 0) {
$this->allHeaders[$name] = $value;
}
}
}
/**
* Get the HTTP headers sent by the server.
*
@ -274,6 +285,66 @@ class RemoteObject extends Object {
return $this->allHeaders;
}
public function additionalHeaders($mergeAll = FALSE) {
// Any additional headers will be set. Note that $this->headers will contain
// some headers that are NOT additional. But we do not know which headers are
// additional and which are from Swift because Swift does not commit to using
// a specific set of headers.
if ($mergeAll) {
$additionalHeaders = parent::additionalHeaders() + $this->allHeaders;
$this->filterHeaders($additionalHeaders);
}
else {
$additionalHeaders = parent::additionalHeaders();
}
return $additionalHeaders;
}
protected $reservedHeaders = array(
'etag' => TRUE, 'content-length' => TRUE,
'x-auth-token' => TRUE,
'transfer-encoding' => TRUE,
'x-trans-id' => TRUE,
);
public function filterHeaders(&$headers) {
$unset = array();
foreach ($headers as $name => $value) {
$lower = strtolower($name);
if (isset($this->reservedHeaders[$lower])) {
$unset[] = $name;
}
}
foreach ($unset as $u) {
unset($headers[$u]);
}
}
/**
* Given an array of header names.
*
* This will remove the given headers from the existing headers.
* Both additional headers and the original headers from the
* server are affected here.
*
* Note that you cannot remove metadata through this mechanism,
* as it is managed using the metadata() methods.
*
* @attention
* Many headers are generated automatically, such as
* Content-Type and Content-Length. Removing these
* will simply result in their being regenerated.
*
* @param array $keys
* The header names to be removed.
*/
public function removeHeaders($keys) {
foreach ($keys as $key) {
unset($this->allHeaders[$key]);
unset($this->additionalHeaders[$key]);
}
}
/**
* Get the content of this object.
*

View File

@ -108,7 +108,8 @@ use \HPCloud\Storage\ObjectStorage;
* array('swift' => array(
* 'account' => ACCOUNT_NUMBER,
* 'key' => SECRET_KEY,
* 'tenantId' => TENANT_ID
* 'tenantid' => TENANT_ID,
* 'tenantname' => TENANT_NAME, // Optional instead of tenantid.
* 'endpoint' => AUTH_ENDPOINT_URL,
* )
* )
@ -218,9 +219,12 @@ use \HPCloud\Storage\ObjectStorage;
* -# User login: username, password, tenantid, endpoint
* -# Existing (valid) token: token, swift_endpoint
*
* @attention
* As of 1.0.0-beta6, you may use `tenantname` instead of `tenantid`.
*
* The third method (token) can be used when the application has already
* authenticated. In this case, a token has been generated and assigneet
* to an account and tenant ID.
* authenticated. In this case, a token has been generated and assigned
* to an account and tenant.
*
* The following parameters may be set either in the stream context
* or through HPCloud::Bootstrap::setConfiguration():
@ -230,10 +234,10 @@ use \HPCloud\Storage\ObjectStorage;
* option.
* - swift_endpoint: The URL to the swift instance. This is only necessary if
* 'token' is set. Otherwise it is ignored.
* - username: A username. MUST be accompanied by 'password' and 'tenantid'.
* - password: A password. MUST be accompanied by 'username' and 'tenantid'.
* - account: An account ID. MUST be accompanied by a 'key' and 'tenantid'.
* - key: A secret key. MUST be accompanied by an 'account' and 'tenantid'.
* - username: A username. MUST be accompanied by 'password' and 'tenantid' (or 'tenantname').
* - password: A password. MUST be accompanied by 'username' and 'tenantid' (or 'tenantname').
* - account: An account ID. MUST be accompanied by a 'key' and 'tenantid' (or 'tenantname').
* - key: A secret key. MUST be accompanied by an 'account' and 'tenantid' (or 'tenantname').
* - endpoint: The URL to the authentication endpoint. Necessary if you are not
* using a 'token' and 'swift_endpoint'.
* - use_swift_auth: If this is set to TRUE, it will force the app to use
@ -251,6 +255,10 @@ use \HPCloud\Storage\ObjectStorage;
* - cdn_require_ssl: If this is set to FALSE, then CDN-based requests
* may use plain HTTP instead of HTTPS. This will spead up CDN
* fetches at the cost of security.
* - tenantid: The tenant ID for the services you will use. (An account may
* have multiple tenancies associated.)
* - tenantname: The tenant name for the services you will use. You may use
* this in lieu of tenant ID.
*
* @attention
* ADVANCED: You can also pass an HPCloud::Storage::CDN object in use_cdn instead of
@ -533,7 +541,8 @@ class StreamWrapper {
* @code
* <?php
* Bootstrap::setConfiguration(array(
* 'tenantid' => '1234',
* 'tenantname' => 'foo@example.com',
* // 'tenantid' => '1234', // You can use this instead of tenantname
* 'account' => '1234',
* 'secret' => '4321',
* 'endpoint' => 'https://auth.example.com',
@ -947,7 +956,7 @@ class StreamWrapper {
* @code
* <?php
* $cxt = stream_context_create(array(
* 'tenantid' => '12345',
* 'tenantname' => 'me@example.com',
* 'username' => 'me@example.com',
* 'password' => 'secret',
* 'endpoint' => 'https://auth.example.com',
@ -1475,10 +1484,10 @@ class StreamWrapper {
* option.
* - swift_endpoint: The URL to the swift instance. This is only necessary if
* 'token' is set. Otherwise it is ignored.
* - username: A username. MUST be accompanied by 'password' and 'tenantid'.
* - password: A password. MUST be accompanied by 'username' and 'tenantid'.
* - account: An account ID. MUST be accompanied by a 'key' and 'tenantid'.
* - key: A secret key. MUST be accompanied by an 'account' and 'tenantid'.
* - username: A username. MUST be accompanied by 'password' and 'tenantname'.
* - password: A password. MUST be accompanied by 'username' and 'tenantname'.
* - account: An account ID. MUST be accompanied by a 'key' and 'tenantname'.
* - key: A secret key. MUST be accompanied by an 'account' and 'tenantname'.
* - endpoint: The URL to the authentication endpoint. Necessary if you are not
* using a 'token' and 'swift_endpoint'.
* - use_swift_auth: If this is set to TRUE, it will force the app to use
@ -1498,6 +1507,7 @@ class StreamWrapper {
$key = $this->cxt('key');
$tenantId = $this->cxt('tenantid');
$tenantName = $this->cxt('tenantname');
$authUrl = $this->cxt('endpoint');
$endpoint = $this->cxt('swift_endpoint');
@ -1522,8 +1532,11 @@ class StreamWrapper {
}
// If we get here and tenant ID is not set, we can't get a container.
elseif (empty($tenantId) || empty($authUrl)) {
throw new \HPCloud\Exception('Tenant ID (tenantid) and endpoint are required.');
elseif (empty($tenantId) && empty($tenantName)) {
throw new \HPCloud\Exception('Either Tenant ID (tenantid) or Tenant Name (tenantname) is required.');
}
elseif (empty($authUrl)) {
throw new \HPCloud\Exception('An Identity Service Endpoint (endpoint) is required.');
}
// Try to authenticate and get a new token.
else {
@ -1612,6 +1625,7 @@ class StreamWrapper {
$key = $this->cxt('key');
$tenantId = $this->cxt('tenantid');
$tenantName = $this->cxt('tenantname');
$authUrl = $this->cxt('endpoint');
$ident = new \HPCloud\Services\IdentityServices($authUrl);
@ -1619,10 +1633,10 @@ class StreamWrapper {
// Frustrated? Go burninate. http://www.homestarrunner.com/trogdor.html
if (!empty($username) && !empty($password)) {
$token = $ident->authenticateAsUser($username, $password, $tenantId);
$token = $ident->authenticateAsUser($username, $password, $tenantId, $tenantName);
}
elseif (!empty($account) && !empty($key)) {
$token = $ident->authenticateAsAccount($account, $key, $tenantId);
$token = $ident->authenticateAsAccount($account, $key, $tenantId, $tenantName);
}
else {
throw new \HPCloud\Exception('Either username/password or account/key must be provided.');

View File

@ -179,12 +179,13 @@ class Response {
}
}
else {
while (!feof($this->handle)) {
$out .= fread($this->handle, 8192);
}
// XXX: This works fine with CURL, but will not
// work with PHP HTTP Stream Wrapper b/c the
// wrapper has a bug that will cause this to
// hang.
$out = stream_get_contents($this->handle);
}
// Should we close or rewind?
// Cannot rewind PHP HTTP streams.
fclose($this->handle);

View File

@ -26,7 +26,8 @@ SOFTWARE.
* You can run the test with `php test/AuthTest.php username key`.
*/
require_once 'src/HPCloud/Bootstrap.php';
$base = dirname(__DIR__);
require_once $base . '/src/HPCloud/Bootstrap.php';
use \HPCloud\Storage\ObjectStorage;
use \HPCloud\Services\IdentityServices;

View File

@ -102,8 +102,8 @@ class TestCase extends \PHPUnit_Framework_TestCase {
$user = self::$settings['hpcloud.swift.account'];
$key = self::$settings['hpcloud.swift.key'];
// $url = self::$settings['hpcloud.swift.url'];
$url = self::$settings['hpcloud.identity.url'];
$url = self::$settings['hpcloud.swift.url'];
//$url = self::$settings['hpcloud.identity.url'];
return \HPCloud\Storage\ObjectStorage::newFromSwiftAuth($user, $key, $url);
@ -145,19 +145,7 @@ class TestCase extends \PHPUnit_Framework_TestCase {
if ($reset || empty(self::$ostore)) {
$ident = $this->identity($reset);
$services = $ident->serviceCatalog(\HPCloud\Storage\ObjectStorage::SERVICE_TYPE);
if (empty($services)) {
throw new \Exception('No object-store service found.');
}
/*
//$serviceURL = $services[0]['endpoints'][0]['adminURL'];
$serviceURL = $services[0]['endpoints'][0]['publicURL'];
$objStore = new \HPCloud\Storage\ObjectStorage($ident->token(), $serviceURL);
*/
$objStore = \HPCloud\Storage\ObjectStorage::newFromServiceCatalog($services, $ident->token());
$objStore = \HPCloud\Storage\ObjectStorage::newFromIdentity($ident);
self::$ostore = $objStore;

View File

@ -80,6 +80,18 @@ class CDNTest extends \HPCloud\Tests\TestCase {
return $cdn;
}
/**
* @depends testConstructor
*/
public function testNewFromIdentity() {
$ident = $this->identity();
$cdn = CDN::newFromIdentity($ident);
$this->assertInstanceOf('\HPCloud\Storage\CDN', $cdn);
return $cdn;
}
/**
* @depends testNewFromServiceCatalog
*/

View File

@ -30,6 +30,7 @@ require_once 'src/HPCloud/Bootstrap.php';
require_once 'test/TestCase.php';
use \HPCloud\Services\IdentityServices;
use \HPCloud\Bootstrap;
class IdentityServicesTest extends \HPCloud\Tests\TestCase {
@ -169,6 +170,48 @@ class IdentityServicesTest extends \HPCloud\Tests\TestCase {
$this->assertNotEmpty($service->token());
}
/**
* @depends testAuthenticateAsAccount
*/
public function testIsExpired($service) {
$this->assertFalse($service->isExpired());
$service2 = new IdentityServices(self::conf('hpcloud.identity.url'));
$this->assertTrue($service2->isExpired());
}
/**
* @depends testAuthenticateAsAccount
*/
public function testTenantName() {
$account = self::conf('hpcloud.identity.account');
$secret = self::conf('hpcloud.identity.secret');
$user = self::conf('hpcloud.identity.username');
$pass = self::conf('hpcloud.identity.password');
$tenantName = self::conf('hpcloud.identity.tenantName');
$service = new IdentityServices(self::conf('hpcloud.identity.url'));
$this->assertNull($service->tenantName());
$service->authenticateAsUser($user, $pass);
$this->assertEmpty($service->tenantName());
$service = new IdentityServices(self::conf('hpcloud.identity.url'));
$ret = $service->authenticateAsUser($user, $pass, NULL, $tenantName);
$this->assertNotEmpty($service->tenantName());
$service = new IdentityServices(self::conf('hpcloud.identity.url'));
$this->assertNull($service->tenantName());
$service->authenticateAsAccount($account, $secret);
$this->assertEmpty($service->tenantName());
$service = new IdentityServices(self::conf('hpcloud.identity.url'));
$ret = $service->authenticateAsAccount($account, $secret, NULL, $tenantName);
$this->assertNotEmpty($service->tenantName());
$this->assertEquals($tenantName, $service->tenantName());
}
/**
* @depends testAuthenticateAsAccount
*/
@ -271,6 +314,33 @@ class IdentityServicesTest extends \HPCloud\Tests\TestCase {
$this->assertNotEmpty($user['roles']);
}
/**
* @depends testAuthenticateAsAccount
* @group serialize
*/
public function testSerialization($service) {
$ser = serialize($service);
$this->assertNotEmpty($ser);
$again = unserialize($ser);
$this->assertInstanceOf('\HPCloud\Services\IdentityServices', $again);
$this->assertEquals($service->tenantId(), $again->tenantId());
$this->assertEquals($service->serviceCatalog(), $again->serviceCatalog());
$this->assertEquals($service->tokenDetails(), $again->tokenDetails());
$this->assertEquals($service->user(), $again->user());
$this->assertFalse($again->isExpired());
$tenantId = $again->tenantId();
$newTok = $again->rescopeUsingTenantId($tenantId);
$this->assertNotEmpty($newTok);
}
/**
* @group tenant
*/
@ -319,7 +389,7 @@ class IdentityServicesTest extends \HPCloud\Tests\TestCase {
$catalog = $service->serviceCatalog();
$this->assertEquals(1, count($catalog));
$service->rescope($tenantId);
$service->rescopeUsingTenantId($tenantId);
$details = $service->tokenDetails();
$this->assertEquals($tenantId, $details['tenant']['id']);
@ -327,6 +397,46 @@ class IdentityServicesTest extends \HPCloud\Tests\TestCase {
$catalog = $service->serviceCatalog();
$this->assertGreaterThan(1, count($catalog));
// Test unscoping
$service->rescopeUsingTenantId('');
$details = $service->tokenDetails();
$this->assertEmpty($details['tenant']);
$catalog = $service->serviceCatalog();
$this->assertEquals(1, count($catalog));
}
/**
* @group tenant
* @depends testTenants
*/
function testRescopeByTenantName() {
$service = new IdentityServices(self::conf('hpcloud.identity.url'));
$user = self::conf('hpcloud.identity.username');
$pass = self::conf('hpcloud.identity.password');
$tenantName = self::conf('hpcloud.identity.tenantName');
// Authenticate without a tenant ID.
$token = $service->authenticateAsUser($user, $pass);
$this->assertNotEmpty($token);
$details = $service->tokenDetails();
$this->assertEmpty($details['tenant']);
// With no tenant ID, there should be only
// one entry in the catalog.
$catalog = $service->serviceCatalog();
$this->assertEquals(1, count($catalog));
$service->rescopeUsingTenantName($tenantName);
$details = $service->tokenDetails();
$this->assertEquals($tenantName, $details['tenant']['name']);
$catalog = $service->serviceCatalog();
$this->assertGreaterThan(1, count($catalog));
// Test unscoping
$service->rescope('');
$details = $service->tokenDetails();
@ -335,4 +445,77 @@ class IdentityServicesTest extends \HPCloud\Tests\TestCase {
$this->assertEquals(1, count($catalog));
}
/**
* Test the bootstrap identity factory.
* @depends testAuthenticateAsAccount
* @depends testAuthenticateAsUser
*/
function testBootstrap() {
// We need to save the config settings and reset the bootstrap to this.
// It does not remove the old settings. The means the identity fall through
// for different settings may not happen because of ordering. So, we cache
// and reset back to the default for each test.
$reset = Bootstrap::$config;
// Test authenticating as a user.
$settings = array(
'username' => self::conf('hpcloud.identity.username'),
'password' => self::conf('hpcloud.identity.password'),
'endpoint' => self::conf('hpcloud.identity.url'),
'tenantid' => self::conf('hpcloud.identity.tenantId'),
);
Bootstrap::setConfiguration($settings);
$is = Bootstrap::identity(TRUE);
$this->assertInstanceOf('\HPCloud\Services\IdentityServices', $is);
Bootstrap::$config = $reset;
// Test authenticating as an account.
$settings = array(
'account' => self::conf('hpcloud.identity.account'),
'secret' => self::conf('hpcloud.identity.secret'),
'endpoint' => self::conf('hpcloud.identity.url'),
'tenantid' => self::conf('hpcloud.identity.tenantId'),
);
Bootstrap::setConfiguration($settings);
$is = Bootstrap::identity(TRUE);
$this->assertInstanceOf('\HPCloud\Services\IdentityServices', $is);
// Test getting a second instance from the cache.
$is2 = Bootstrap::identity();
$this->assertEquals($is, $is2);
// Test that forcing a refresh does so.
$is2 = Bootstrap::identity(TRUE);
$this->assertNotEquals($is, $is2);
Bootstrap::$config = $reset;
// Test with tenant name
$settings = array(
'account' => self::conf('hpcloud.identity.account'),
'secret' => self::conf('hpcloud.identity.secret'),
'endpoint' => self::conf('hpcloud.identity.url'),
'tenantname' => self::conf('hpcloud.identity.tenantName'),
);
Bootstrap::setConfiguration($settings);
$is = Bootstrap::identity(TRUE);
$this->assertInstanceOf('\HPCloud\Services\IdentityServices', $is);
$settings = array(
'username' => self::conf('hpcloud.identity.username'),
'password' => self::conf('hpcloud.identity.password'),
'endpoint' => self::conf('hpcloud.identity.url'),
'tenantname' => self::conf('hpcloud.identity.tenantName'),
);
Bootstrap::setConfiguration($settings);
$is = Bootstrap::identity(TRUE);
$this->assertInstanceOf('\HPCloud\Services\IdentityServices', $is);
}
}

View File

@ -77,7 +77,17 @@ class ObjectStorageTest extends \HPCloud\Tests\TestCase {
}
public function testNewFromServiceCatalog() {
$ostore = $this->objectStore();
$ident = $this->identity();
$tok = $ident->token();
$cat = $ident->serviceCatalog();
$ostore = \HPCloud\Storage\ObjectStorage::newFromServiceCatalog($cat, $tok);
$this->assertInstanceOf('\HPCloud\Storage\ObjectStorage', $ostore);
$this->assertTrue(strlen($ostore->token()) > 0);
}
public function testNewFromIdnetity() {
$ident = $this->identity();
$ostore = \HPCloud\Storage\ObjectStorage::newFromIdentity($ident);
$this->assertInstanceOf('\HPCloud\Storage\ObjectStorage', $ostore);
$this->assertTrue(strlen($ostore->token()) > 0);
}

View File

@ -131,4 +131,24 @@ class ObjectTest extends \HPCloud\Tests\TestCase {
$this->assertEquals('Leibniz', $got['Gottfried']);
}
public function testAdditionalHeaders() {
$o = $this->basicObjectFixture();
$extra = array(
'a' => 'b',
'aaa' => 'bbb',
'ccc' => 'bbb',
);
$o->setAdditionalHeaders($extra);
$got = $o->additionalHeaders();
$this->assertEquals(3, count($got));
$o->removeHeaders(array('ccc'));
$got = $o->additionalHeaders();
$this->assertEquals(2, count($got));
}
}

View File

@ -53,6 +53,10 @@ class RemoteObjectTest extends \HPCloud\Tests\TestCase {
$object->setMetadata(array(self::FMETA_NAME => self::FMETA_VALUE));
$object->setDisposition(self::FDISPOSITION);
$object->setEncoding(self::FENCODING);
$object->setAdditionalHeaders(array(
'Access-Control-Allow-Origin' => 'http://example.com',
'Access-control-allow-origin' => 'http://example.com',
));
// Need some headers that Swift actually stores and returns. This
// one does not seem to be returned ever.
@ -146,6 +150,15 @@ class RemoteObjectTest extends \HPCloud\Tests\TestCase {
$headers = $obj->headers();
$this->assertTrue(count($headers) > 1);
//fwrite(STDOUT, print_r($headers, TRUE));
$this->assertNotEmpty($headers['Date']);
$obj->removeHeaders(array('Date'));
$headers = $obj->headers();
$this->assertFalse(isset($headers['Date']));
// Swift doesn't return CORS headers even though it is supposed to.
//$this->assertEquals(self::FCORS_VALUE, $headers[self::FCORS_NAME]);
}

View File

@ -45,6 +45,46 @@ class StreamWrapperFSTest extends \HPCloud\Tests\TestCase {
/*public static function setUpBeforeClass() {
}*/
/**
* Cleaning up the test container so we can reuse it for other tests.
*/
public static function tearDownAfterClass() {
// First we get an identity
$user = self::conf('hpcloud.identity.username');
$pass = self::conf('hpcloud.identity.password');
$tenantId = self::conf('hpcloud.identity.tenantId');
$url = self::conf('hpcloud.identity.url');
$ident = new \HPCloud\Services\IdentityServices($url);
$token = $ident->authenticateAsUser($user, $pass, $tenantId);
// Then we need to get an instance of storage
$store = \HPCloud\Storage\ObjectStorage::newFromIdentity($ident);
// Delete the container and all the contents.
$cname = self::$settings['hpcloud.swift.container'];
try {
$container = $store->container($cname);
}
// The container was never created.
catch (\HPCloud\Transport\FileNotFoundException $e) {
return;
}
foreach ($container as $object) {
try {
$container->delete($object->name());
}
catch (\Exception $e) {}
}
$store->deleteContainer($cname);
}
protected function newUrl($objectName) {
$scheme = StreamWrapperFS::DEFAULT_SCHEME;
$cname = self::$settings['hpcloud.swift.container'];
@ -129,7 +169,7 @@ class StreamWrapperFSTest extends \HPCloud\Tests\TestCase {
'account' => self::$settings['hpcloud.identity.account'],
'key' => self::$settings['hpcloud.identity.secret'],
'endpoint' => self::$settings['hpcloud.identity.url'],
'tenantit' => self::$settings['hpcloud.identity.tenantId'],
'tenantid' => self::$settings['hpcloud.identity.tenantId'],
'token' => $this->objectStore()->token(),
'swift_endpoint' => $this->objectStore()->url(),
);

View File

@ -42,6 +42,46 @@ class StreamWrapperTest extends \HPCloud\Tests\TestCase {
const FNAME = 'streamTest.txt';
const FTYPE = 'application/x-tuna-fish; charset=iso-8859-13';
/**
* Cleaning up the test container so we can reuse it for other tests.
*/
public static function tearDownAfterClass() {
// First we get an identity
$user = self::conf('hpcloud.identity.username');
$pass = self::conf('hpcloud.identity.password');
$tenantId = self::conf('hpcloud.identity.tenantId');
$url = self::conf('hpcloud.identity.url');
$ident = new \HPCloud\Services\IdentityServices($url);
$token = $ident->authenticateAsUser($user, $pass, $tenantId);
// Then we need to get an instance of storage
$store = \HPCloud\Storage\ObjectStorage::newFromIdentity($ident);
// Delete the container and all the contents.
$cname = self::$settings['hpcloud.swift.container'];
try {
$container = $store->container($cname);
}
// The container was never created.
catch (\HPCloud\Transport\FileNotFoundException $e) {
return;
}
foreach ($container as $object) {
try {
$container->delete($object->name());
}
catch (\Exception $e) {}
}
$store->deleteContainer($cname);
}
protected function newUrl($objectName) {
$scheme = StreamWrapper::DEFAULT_SCHEME;
$cname = self::$settings['hpcloud.swift.container'];
@ -126,7 +166,7 @@ class StreamWrapperTest extends \HPCloud\Tests\TestCase {
'account' => self::$settings['hpcloud.identity.account'],
'key' => self::$settings['hpcloud.identity.secret'],
'endpoint' => self::$settings['hpcloud.identity.url'],
'tenantit' => self::$settings['hpcloud.identity.tenantId'],
'tenantid' => self::$settings['hpcloud.identity.tenantId'],
'token' => $this->objectStore()->token(),
'swift_endpoint' => $this->objectStore()->url(),
);

View File

@ -4,9 +4,13 @@
;;;;;;;;;;;;;;;;;;
; Settings to work with swift:
; Account is the tenandId:console username.
hpcloud.swift.account = 12345678:87654321
; Key is the console account password.
hpcloud.swift.key = abcdef123456
hpcloud.swift.url = https://region-a.geo-1.objects.hpcloudsvc.com/auth/v1.0/
; URL is the same as used for identity services calls (including port) except
; with /auth/v1.0/ appended to the end.
hpcloud.swift.url = https://region-a.geo-1.identity.hpcloudsvc.com:35357/auth/v1.0/
; Container used for testing.
hpcloud.swift.container = "I♡HPCloud"