diff --git a/src/ObjectStore/v1/Service.php b/src/ObjectStore/v1/Service.php index 27ba97c3..d81e9d85 100644 --- a/src/ObjectStore/v1/Service.php +++ b/src/ObjectStore/v1/Service.php @@ -86,4 +86,42 @@ public function containerExists(string $name): bool throw $e; } } + + /** + * Creates a temporary URL to access object in private containers. + * This method loosely follows swift command's way to generate temporary url: `swift tempurl $METHOD $EXPIRE $PATH $KEY`. + * + * @param string $method An HTTP method to allow for this temporary URL. Any of GET, POST, HEAD, PUT, POST, DELETE. + * @param int $expires Unix timestamp + * @param string $path The full path or storage URL to the Swift object. Example: '/v1/AUTH_account/c/o' or: 'http://saio:8080/v1/AUTH_account/c/o' + * For prefix based signature, set path to 'prefix:/v1/AUTH_account/container/pre' + * @param string $key The secret temporary URL key set on the Swift cluster* + * @param string $ipRange [OPTIONAL] If present, the temporary URL will be restricted to the given ip or ip range + * @param string $digest [OPTIONAL] The digest algorithm to be used may be configured by the operator. Default to sha1. + * Check the tempurl.allowed_digests entry in the cluster's capabilities response to see which algorithms are supported by your + * deployment; + * + * @return string + * + * @throws \RuntimeException + */ + public function tempUrl(string $method, int $expires, string $path, string $key, string $ipRange = null, string $digest = 'sha1'): string + { + if (!function_exists('hash_hmac')) { + throw new \RuntimeException(sprintf('tempUrl requires hash extension enabled.')); + } + + if ($ipRange) { + $message = sprintf("ip=%s\n%s\n%s\n%s", $ipRange, $method, $expires, $path); + } else { + $message = sprintf("%s\n%s\n%s", $method, $expires, $path); + } + + $signature = hash_hmac($digest, $message, $key); + + // sha512 requires prefixing signature + $signature = 'sha512' === $digest ? 'sha512:'.$signature : $signature; + + return sprintf('%s?temp_url_sig=%s&temp_url_expires=%s', $path, $signature, $expires); + } } diff --git a/tests/unit/ObjectStore/v1/ServiceTest.php b/tests/unit/ObjectStore/v1/ServiceTest.php index c4f4717d..5a0d3046 100644 --- a/tests/unit/ObjectStore/v1/ServiceTest.php +++ b/tests/unit/ObjectStore/v1/ServiceTest.php @@ -13,6 +13,7 @@ class ServiceTest extends TestCase { + /** @var Service */ private $service; public function setUp() @@ -84,4 +85,31 @@ public function test_it_throws_exception_when_error() $this->assertFalse($this->service->containerExists('foo')); } + + public function test_it_generates_temp_url_sha1() + { + $cases = [ + [ + ['GET', '1516741234', '/v1/AUTH_account/container/object', 'mykey'], + '/v1/AUTH_account/container/object?temp_url_sig=712dcef48d391e39bd2e3b63fc0a07146a36055e&temp_url_expires=1516741234' + ], + [ + ['HEAD', '1516741234', '/v1/AUTH_account/container/object', 'somekey'], + '/v1/AUTH_account/container/object?temp_url_sig=a4516e93f2023652641fec44c82163dc298620e8&temp_url_expires=1516741234' + ], + [ + ['GET', '1323479485', 'prefix:/v1/AUTH_account/container/pre', 'mykey'], + '/v1/AUTH_account/container/object?temp_url_sig=a4516e93f2023652641fec44c82163dc298620e8&temp_url_expires=1516741234' + ] + ]; + + foreach ($cases as $case) + { + $params = $case[0]; + $expected = $case[1]; + + $actual = call_user_func_array([$this->service, 'tempUrl'], $params); + $this->assertEquals($expected, $actual); + } + } }