From 5062d0ac50facc1944fe9e721c19164675894f8b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 28 Mar 2017 11:02:18 +0200 Subject: [PATCH 1/2] better error messages when swift authentication fails Signed-off-by: Robin Appelman --- lib/private/Files/ObjectStore/Swift.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/private/Files/ObjectStore/Swift.php b/lib/private/Files/ObjectStore/Swift.php index 2ccaad27e8..4f994b6f40 100644 --- a/lib/private/Files/ObjectStore/Swift.php +++ b/lib/private/Files/ObjectStore/Swift.php @@ -26,6 +26,8 @@ namespace OC\Files\ObjectStore; use Guzzle\Http\Exception\ClientErrorResponseException; use OCP\Files\ObjectStore\IObjectStore; +use OCP\Files\StorageAuthException; +use OpenCloud\Common\Exceptions\EndpointError; use OpenCloud\OpenStack; use OpenCloud\Rackspace; @@ -76,6 +78,19 @@ class Swift implements IObjectStore { return; } + try { + $this->client->authenticate(); + } catch (ClientErrorResponseException $e) { + $statusCode = $e->getResponse()->getStatusCode(); + if ($statusCode == 412) { + throw new StorageAuthException('Precondition failed, verify the keystone url', $e); + } else if ($statusCode === 401) { + throw new StorageAuthException('Authentication failed, verify the username, password and possibly tenant', $e); + } else { + throw new StorageAuthException('Unknown error', $e); + } + } + // the OpenCloud client library will default to 'cloudFiles' if $serviceName is null $serviceName = null; if (isset($this->params['serviceName'])) { @@ -87,6 +102,7 @@ class Swift implements IObjectStore { if (isset($this->params['urlType'])) { $urlType = $this->params['urlType']; } + $this->objectStoreService = $this->client->objectStoreService($serviceName, $this->params['region'], $urlType); try { @@ -135,7 +151,7 @@ class Swift implements IObjectStore { $stream = $objectContent->getStream(); // save the object content in the context of the stream to prevent it being gc'd until the stream is closed - stream_context_set_option($stream, 'swift','content', $objectContent); + stream_context_set_option($stream, 'swift', 'content', $objectContent); return $stream; } From 8dbca71a77418b3f7ccd8be8a5dc0ab93858513e Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 28 Mar 2017 11:39:25 +0200 Subject: [PATCH 2/2] better error messages for invalid regions, urltypes and service names Signed-off-by: Robin Appelman --- lib/private/Files/ObjectStore/Swift.php | 67 +++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/lib/private/Files/ObjectStore/Swift.php b/lib/private/Files/ObjectStore/Swift.php index 4f994b6f40..43ce32af94 100644 --- a/lib/private/Files/ObjectStore/Swift.php +++ b/lib/private/Files/ObjectStore/Swift.php @@ -27,7 +27,11 @@ namespace OC\Files\ObjectStore; use Guzzle\Http\Exception\ClientErrorResponseException; use OCP\Files\ObjectStore\IObjectStore; use OCP\Files\StorageAuthException; +use OCP\Files\StorageNotAvailableException; use OpenCloud\Common\Exceptions\EndpointError; +use OpenCloud\Common\Service\Catalog; +use OpenCloud\Common\Service\CatalogItem; +use OpenCloud\ObjectStore\Service; use OpenCloud\OpenStack; use OpenCloud\Rackspace; @@ -91,16 +95,32 @@ class Swift implements IObjectStore { } } - // the OpenCloud client library will default to 'cloudFiles' if $serviceName is null - $serviceName = null; + /** @var Catalog $catalog */ + $catalog = $this->client->getCatalog(); + if (isset($this->params['serviceName'])) { $serviceName = $this->params['serviceName']; + } else { + $serviceName = Service::DEFAULT_NAME; } - // the OpenCloud client library will default to 'publicURL' if $urlType is null - $urlType = null; if (isset($this->params['urlType'])) { $urlType = $this->params['urlType']; + if ($urlType !== 'internalURL' && $urlType !== 'publicURL') { + throw new StorageNotAvailableException('Invalid url type'); + } + } else { + $urlType = Service::DEFAULT_URL_TYPE; + } + + $catalogItem = $this->getCatalogForService($catalog, $serviceName); + if (!$catalogItem) { + $available = implode(', ', $this->getAvailableServiceNames($catalog)); + throw new StorageNotAvailableException( + "Service $serviceName not found in service catalog, available services: $available" + ); + } else if (isset($this->params['region'])) { + $this->validateRegion($catalogItem, $this->params['region']); } $this->objectStoreService = $this->client->objectStoreService($serviceName, $this->params['region'], $urlType); @@ -117,6 +137,45 @@ class Swift implements IObjectStore { } } + /** + * @param Catalog $catalog + * @param $name + * @return null|CatalogItem + */ + private function getCatalogForService(Catalog $catalog, $name) { + foreach ($catalog->getItems() as $item) { + /** @var CatalogItem $item */ + if ($item->hasType(Service::DEFAULT_TYPE) && $item->hasName($name)) { + return $item; + } + } + + return null; + } + + private function validateRegion(CatalogItem $item, $region) { + $endPoints = $item->getEndpoints(); + foreach ($endPoints as $endPoint) { + if ($endPoint->region === $region) { + return; + } + } + + $availableRegions = implode(', ', array_map(function ($endpoint) { + return $endpoint->region; + }, $endPoints)); + + throw new StorageNotAvailableException("Invalid region '$region', available regions: $availableRegions"); + } + + private function getAvailableServiceNames(Catalog $catalog) { + return array_map(function (CatalogItem $item) { + return $item->getName(); + }, array_filter($catalog->getItems(), function (CatalogItem $item) { + return $item->hasType(Service::DEFAULT_TYPE); + })); + } + /** * @return string the container name where objects are stored */