/libraries/Zend/Service/WindowsAzure/Credentials/SharedAccessSignature.php

https://github.com/kiranatama/sagalaya · PHP · 288 lines · 145 code · 30 blank · 113 comment · 19 complexity · 3f27e201754831a0a24ec75f6facd425 MD5 · raw file

  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. * @package Zend_Service_WindowsAzure
  9. */
  10. namespace Zend\Service\WindowsAzure\Credentials;
  11. use Zend\Http\Request;
  12. use Zend\Service\WindowsAzure\Storage;
  13. use Zend\Service\WindowsAzure\Exception\DomainException;
  14. /**
  15. * @category Zend
  16. * @package Zend_Service_WindowsAzure
  17. */
  18. class SharedAccessSignature extends AbstractCredentials
  19. {
  20. /**
  21. * Permission set
  22. *
  23. * @var array
  24. */
  25. protected $_permissionSet = array();
  26. /**
  27. * @param string $accountName Account name for Windows Azure
  28. * @param string $accountKey Account key for Windows Azure
  29. * @param boolean $usePathStyleUri Use path-style URI's
  30. * @param array $permissionSet Permission set
  31. */
  32. public function __construct(
  33. $accountName = AbstractCredentials::DEVSTORE_ACCOUNT,
  34. $accountKey = AbstractCredentials::DEVSTORE_KEY,
  35. $usePathStyleUri = false, $permissionSet = array()
  36. )
  37. {
  38. parent::__construct($accountName, $accountKey, $usePathStyleUri);
  39. $this->_permissionSet = $permissionSet;
  40. }
  41. /**
  42. * Get permission set
  43. *
  44. * @return array
  45. */
  46. public function getPermissionSet()
  47. {
  48. return $this->_permissionSet;
  49. }
  50. /**
  51. * Set permission set
  52. *
  53. * Warning: fine-grained permissions should be added prior to coarse-grained permissions.
  54. * For example: first add blob permissions, end with container-wide permissions.
  55. *
  56. * Warning: the signed access signature URL must match the account name of the
  57. * SharedAccessSignature instance
  58. *
  59. * @param array $value Permission set
  60. * @throws DomainException
  61. * @return void
  62. */
  63. public function setPermissionSet($value = array())
  64. {
  65. foreach ($value as $url) {
  66. if (strpos($url, $this->_accountName) === false) {
  67. throw new DomainException('The permission set can only contain URLs for the account name specified in the SharedAccessSignature instance.');
  68. }
  69. }
  70. $this->_permissionSet = $value;
  71. }
  72. /**
  73. * Create signature
  74. *
  75. * @param string $path Path for the request
  76. * @param string $resource Signed resource - container (c) - blob (b)
  77. * @param string $permissions Signed permissions - read (r), write (w), delete (d) and list (l)
  78. * @param string $start The time at which the Shared Access Signature becomes valid.
  79. * @param string $expiry The time at which the Shared Access Signature becomes invalid.
  80. * @param string $identifier Signed identifier
  81. * @return string
  82. */
  83. public function createSignature(
  84. $path = '/',
  85. $resource = 'b',
  86. $permissions = 'r',
  87. $start = '',
  88. $expiry = '',
  89. $identifier = ''
  90. )
  91. {
  92. // Determine path
  93. if ($this->_usePathStyleUri) {
  94. $path = substr($path, strpos($path, '/'));
  95. }
  96. // Add trailing slash to $path
  97. if (substr($path, 0, 1) !== '/') {
  98. $path = '/' . $path;
  99. }
  100. // Build canonicalized resource string
  101. $canonicalizedResource = '/' . $this->_accountName;
  102. /*if ($this->_usePathStyleUri) {
  103. $canonicalizedResource .= '/' . $this->_accountName;
  104. }*/
  105. $canonicalizedResource .= $path;
  106. // Create string to sign
  107. $stringToSign = array();
  108. $stringToSign[] = $permissions;
  109. $stringToSign[] = $start;
  110. $stringToSign[] = $expiry;
  111. $stringToSign[] = $canonicalizedResource;
  112. $stringToSign[] = $identifier;
  113. $stringToSign = implode("\n", $stringToSign);
  114. $signature = base64_encode(hash_hmac('sha256', $stringToSign, $this->_accountKey, true));
  115. return $signature;
  116. }
  117. /**
  118. * Create signed query string
  119. *
  120. * @param string $path Path for the request
  121. * @param string $queryString Query string for the request
  122. * @param string $resource Signed resource - container (c) - blob (b)
  123. * @param string $permissions Signed permissions - read (r), write (w), delete (d) and list (l)
  124. * @param string $start The time at which the Shared Access Signature becomes valid.
  125. * @param string $expiry The time at which the Shared Access Signature becomes invalid.
  126. * @param string $identifier Signed identifier
  127. * @return string
  128. */
  129. public function createSignedQueryString(
  130. $path = '/',
  131. $queryString = '',
  132. $resource = 'b',
  133. $permissions = 'r',
  134. $start = '',
  135. $expiry = '',
  136. $identifier = ''
  137. )
  138. {
  139. // Parts
  140. $parts = array();
  141. if ($start !== '') {
  142. $parts[] = 'st=' . urlencode($start);
  143. }
  144. $parts[] = 'se=' . urlencode($expiry);
  145. $parts[] = 'sr=' . $resource;
  146. $parts[] = 'sp=' . $permissions;
  147. if ($identifier !== '') {
  148. $parts[] = 'si=' . urlencode($identifier);
  149. }
  150. $parts[] =
  151. 'sig=' . urlencode($this->createSignature($path, $resource, $permissions, $start, $expiry, $identifier));
  152. // Assemble parts and query string
  153. if ($queryString != '') {
  154. $queryString .= '&';
  155. }
  156. $queryString .= implode('&', $parts);
  157. return $queryString;
  158. }
  159. /**
  160. * Permission matches request?
  161. *
  162. * @param string $permissionUrl Permission URL
  163. * @param string $requestUrl Request URL
  164. * @param string $resourceType Resource type
  165. * @param string $requiredPermission Required permission
  166. * @return string Signed request URL
  167. */
  168. public function permissionMatchesRequest(
  169. $permissionUrl = '',
  170. $requestUrl = '',
  171. $resourceType = Storage\Storage::RESOURCE_UNKNOWN,
  172. $requiredPermission = AbstractCredentials::PERMISSION_READ
  173. )
  174. {
  175. // Build requirements
  176. $requiredResourceType = $resourceType;
  177. if ($requiredResourceType == Storage\Storage::RESOURCE_BLOB) {
  178. $requiredResourceType .= Storage\Storage::RESOURCE_CONTAINER;
  179. }
  180. // Parse permission url
  181. $parsedPermissionUrl = parse_url($permissionUrl);
  182. // Parse permission properties
  183. $permissionParts = explode('&', $parsedPermissionUrl['query']);
  184. // Parse request url
  185. $parsedRequestUrl = parse_url($requestUrl);
  186. // Check if permission matches request
  187. $matches = true;
  188. foreach ($permissionParts as $part) {
  189. list($property, $value) = explode('=', $part, 2);
  190. if ($property == 'sr') {
  191. $matches = $matches && (strpbrk($value, $requiredResourceType) !== false);
  192. }
  193. if ($property == 'sp') {
  194. $matches = $matches && (strpbrk($value, $requiredPermission) !== false);
  195. }
  196. }
  197. // Ok, but... does the resource match?
  198. $matches = $matches && (strpos($parsedRequestUrl['path'], $parsedPermissionUrl['path']) !== false);
  199. // Return
  200. return $matches;
  201. }
  202. /**
  203. * Sign request URL with credentials
  204. *
  205. * @param string $requestUrl Request URL
  206. * @param string $resourceType Resource type
  207. * @param string $requiredPermission Required permission
  208. * @return string Signed request URL
  209. */
  210. public function signRequestUrl(
  211. $requestUrl = '',
  212. $resourceType = Storage\Storage::RESOURCE_UNKNOWN,
  213. $requiredPermission = AbstractCredentials::PERMISSION_READ
  214. )
  215. {
  216. // Look for a matching permission
  217. foreach ($this->getPermissionSet() as $permittedUrl) {
  218. if ($this->permissionMatchesRequest($permittedUrl, $requestUrl, $resourceType, $requiredPermission)) {
  219. // This matches, append signature data
  220. $parsedPermittedUrl = parse_url($permittedUrl);
  221. if (strpos($requestUrl, '?') === false) {
  222. $requestUrl .= '?';
  223. } else {
  224. $requestUrl .= '&';
  225. }
  226. $requestUrl .= $parsedPermittedUrl['query'];
  227. // Return url
  228. return $requestUrl;
  229. }
  230. }
  231. // Return url, will be unsigned...
  232. return $requestUrl;
  233. }
  234. /**
  235. * Sign request with credentials
  236. *
  237. * @param string $httpVerb HTTP verb the request will use
  238. * @param string $path Path for the request
  239. * @param string $queryString Query string for the request
  240. * @param array $headers x-ms headers to add
  241. * @param boolean $forTableStorage Is the request for table storage?
  242. * @param string $resourceType Resource type
  243. * @param string $requiredPermission Required permission
  244. * @return array Array of headers
  245. */
  246. public function signRequestHeaders(
  247. $httpVerb = Request::METHOD_GET,
  248. $path = '/',
  249. $queryString = '',
  250. $headers = null,
  251. $forTableStorage = false,
  252. $resourceType = Storage\Storage::RESOURCE_UNKNOWN,
  253. $requiredPermission = AbstractCredentials::PERMISSION_READ
  254. )
  255. {
  256. return $headers;
  257. }
  258. }