File indexing completed on 2024-06-23 05:55:44

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Service_WindowsAzure
0017  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0018  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0019  * @version    $Id$
0020  */
0021 
0022 /**
0023  * @see Zend_Service_WindowsAzure_Credentials_CredentialsAbstract
0024  */
0025 // require_once 'Zend/Service/WindowsAzure/Credentials/CredentialsAbstract.php';
0026 
0027 /**
0028  * @category   Zend
0029  * @package    Zend_Service_WindowsAzure
0030  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0031  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0032  */ 
0033 class Zend_Service_WindowsAzure_Credentials_SharedAccessSignature
0034     extends Zend_Service_WindowsAzure_Credentials_CredentialsAbstract
0035 {
0036     /**
0037      * Permission set
0038      * 
0039      * @var array
0040      */
0041     protected $_permissionSet = array();
0042     
0043   /**
0044    * Creates a new Zend_Service_WindowsAzure_Credentials_SharedAccessSignature instance
0045    *
0046    * @param string $accountName Account name for Windows Azure
0047    * @param string $accountKey Account key for Windows Azure
0048    * @param boolean $usePathStyleUri Use path-style URI's
0049    * @param array $permissionSet Permission set
0050    */
0051   public function __construct(
0052     $accountName = Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::DEVSTORE_ACCOUNT,
0053     $accountKey  = Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::DEVSTORE_KEY,
0054     $usePathStyleUri = false, $permissionSet = array()
0055   ) {
0056       parent::__construct($accountName, $accountKey, $usePathStyleUri);
0057       $this->_permissionSet = $permissionSet;
0058   }
0059   
0060   /**
0061    * Get permission set
0062    * 
0063    * @return array
0064    */
0065     public function getPermissionSet()
0066   {
0067       return $this->_permissionSet;   
0068   }
0069   
0070   /**
0071    * Set permisison set
0072    * 
0073    * Warning: fine-grained permissions should be added prior to coarse-grained permissions.
0074    * For example: first add blob permissions, end with container-wide permissions.
0075    * 
0076    * Warning: the signed access signature URL must match the account name of the
0077    * Zend_Service_WindowsAzure_Credentials_Zend_Service_WindowsAzure_Credentials_SharedAccessSignature instance
0078    * 
0079    * @param  array $value Permission set
0080    * @return void
0081    */
0082     public function setPermissionSet($value = array())
0083   {
0084     foreach ($value as $url) {
0085       if (strpos($url, $this->_accountName) === false) {
0086         // require_once 'Zend/Service/WindowsAzure/Exception.php';
0087         throw new Zend_Service_WindowsAzure_Exception('The permission set can only contain URLs for the account name specified in the Zend_Service_WindowsAzure_Credentials_SharedAccessSignature instance.');
0088       }
0089     }
0090       $this->_permissionSet = $value;
0091   }
0092     
0093     /**
0094      * Create signature
0095      * 
0096      * @param string $path       Path for the request
0097      * @param string $resource     Signed resource - container (c) - blob (b)
0098      * @param string $permissions  Signed permissions - read (r), write (w), delete (d) and list (l)
0099      * @param string $start        The time at which the Shared Access Signature becomes valid.
0100      * @param string $expiry       The time at which the Shared Access Signature becomes invalid.
0101      * @param string $identifier   Signed identifier
0102      * @return string 
0103      */
0104     public function createSignature(
0105       $path = '/',
0106       $resource = 'b',
0107       $permissions = 'r',
0108       $start = '',
0109       $expiry = '',
0110       $identifier = ''
0111     ) {
0112     // Determine path
0113     if ($this->_usePathStyleUri) {
0114       $path = substr($path, strpos($path, '/'));
0115     }
0116       
0117     // Add trailing slash to $path
0118     if (substr($path, 0, 1) !== '/') {
0119         $path = '/' . $path;
0120     }
0121 
0122     // Build canonicalized resource string
0123     $canonicalizedResource  = '/' . $this->_accountName;
0124     /*if ($this->_usePathStyleUri) {
0125       $canonicalizedResource .= '/' . $this->_accountName;
0126     }*/
0127     $canonicalizedResource .= $path;
0128         
0129     // Create string to sign   
0130     $stringToSign   = array();
0131     $stringToSign[] = $permissions;
0132       $stringToSign[] = $start;
0133       $stringToSign[] = $expiry;
0134       $stringToSign[] = $canonicalizedResource;
0135       $stringToSign[] = $identifier;
0136 
0137       $stringToSign = implode("\n", $stringToSign);
0138       $signature    = base64_encode(hash_hmac('sha256', $stringToSign, $this->_accountKey, true));
0139   
0140       return $signature;
0141     }
0142 
0143     /**
0144      * Create signed query string
0145      * 
0146      * @param string $path       Path for the request
0147      * @param string $queryString  Query string for the request
0148      * @param string $resource     Signed resource - container (c) - blob (b)
0149      * @param string $permissions  Signed permissions - read (r), write (w), delete (d) and list (l)
0150      * @param string $start        The time at which the Shared Access Signature becomes valid.
0151      * @param string $expiry       The time at which the Shared Access Signature becomes invalid.
0152      * @param string $identifier   Signed identifier
0153      * @return string 
0154      */
0155     public function createSignedQueryString(
0156       $path = '/',
0157       $queryString = '',
0158       $resource = 'b',
0159       $permissions = 'r',
0160       $start = '',
0161       $expiry = '',
0162       $identifier = ''
0163     ) {
0164         // Parts
0165         $parts = array();
0166         if ($start !== '') {
0167             $parts[] = 'st=' . urlencode($start);
0168         }
0169         $parts[] = 'se=' . urlencode($expiry);
0170         $parts[] = 'sr=' . $resource;
0171         $parts[] = 'sp=' . $permissions;
0172         if ($identifier !== '') {
0173             $parts[] = 'si=' . urlencode($identifier);
0174         }
0175         $parts[] = 'sig=' . urlencode($this->createSignature($path, $resource, $permissions, $start, $expiry, $identifier));
0176 
0177         // Assemble parts and query string
0178         if ($queryString != '') {
0179             $queryString .= '&';
0180       }
0181         $queryString .= implode('&', $parts);
0182 
0183         return $queryString;
0184     }
0185     
0186     /**
0187    * Permission matches request?
0188    *
0189    * @param string $permissionUrl Permission URL
0190    * @param string $requestUrl Request URL
0191    * @param string $resourceType Resource type
0192    * @param string $requiredPermission Required permission
0193    * @return string Signed request URL
0194    */
0195     public function permissionMatchesRequest(
0196       $permissionUrl = '',
0197       $requestUrl = '',
0198       $resourceType = Zend_Service_WindowsAzure_Storage::RESOURCE_UNKNOWN,
0199       $requiredPermission = Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_READ
0200     ) {
0201         // Build requirements
0202         $requiredResourceType = $resourceType;
0203         if ($requiredResourceType == Zend_Service_WindowsAzure_Storage::RESOURCE_BLOB) {
0204             $requiredResourceType .= Zend_Service_WindowsAzure_Storage::RESOURCE_CONTAINER;
0205         }
0206 
0207         // Parse permission url
0208       $parsedPermissionUrl = parse_url($permissionUrl);
0209       
0210       // Parse permission properties
0211       $permissionParts = explode('&', $parsedPermissionUrl['query']);
0212       
0213       // Parse request url
0214       $parsedRequestUrl = parse_url($requestUrl);
0215       
0216       // Check if permission matches request
0217       $matches = true;
0218       foreach ($permissionParts as $part) {
0219           list($property, $value) = explode('=', $part, 2);
0220           
0221           if ($property == 'sr') {
0222               $matches = $matches && (strpbrk($value, $requiredResourceType) !== false);
0223           }
0224           
0225         if ($property == 'sp') {
0226               $matches = $matches && (strpbrk($value, $requiredPermission) !== false);
0227           }
0228       }
0229       
0230       // Ok, but... does the resource match?
0231       $matches = $matches && (strpos($parsedRequestUrl['path'], $parsedPermissionUrl['path']) !== false);
0232       
0233         // Return
0234       return $matches;
0235     }    
0236     
0237     /**
0238    * Sign request URL with credentials
0239    *
0240    * @param string $requestUrl Request URL
0241    * @param string $resourceType Resource type
0242    * @param string $requiredPermission Required permission
0243    * @return string Signed request URL
0244    */
0245   public function signRequestUrl(
0246     $requestUrl = '',
0247     $resourceType = Zend_Service_WindowsAzure_Storage::RESOURCE_UNKNOWN,
0248     $requiredPermission = Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_READ
0249   ) {
0250       // Look for a matching permission
0251       foreach ($this->getPermissionSet() as $permittedUrl) {
0252           if ($this->permissionMatchesRequest($permittedUrl, $requestUrl, $resourceType, $requiredPermission)) {
0253               // This matches, append signature data
0254               $parsedPermittedUrl = parse_url($permittedUrl);
0255 
0256               if (strpos($requestUrl, '?') === false) {
0257                   $requestUrl .= '?';
0258               } else {
0259                   $requestUrl .= '&';
0260               }
0261               
0262               $requestUrl .= $parsedPermittedUrl['query'];
0263 
0264               // Return url
0265               return $requestUrl;
0266           }
0267       }
0268       
0269       // Return url, will be unsigned...
0270       return $requestUrl;
0271   }
0272     
0273   /**
0274    * Sign request with credentials
0275    *
0276    * @param string $httpVerb HTTP verb the request will use
0277    * @param string $path Path for the request
0278    * @param string $queryString Query string for the request
0279    * @param array $headers x-ms headers to add
0280    * @param boolean $forTableStorage Is the request for table storage?
0281    * @param string $resourceType Resource type
0282    * @param string $requiredPermission Required permission
0283    * @param mixed  $rawData Raw post data
0284    * @return array Array of headers
0285    */
0286   public function signRequestHeaders(
0287     $httpVerb = Zend_Http_Client::GET,
0288     $path = '/',
0289     $queryString = '',
0290     $headers = null,
0291     $forTableStorage = false,
0292     $resourceType = Zend_Service_WindowsAzure_Storage::RESOURCE_UNKNOWN,
0293     $requiredPermission = Zend_Service_WindowsAzure_Credentials_CredentialsAbstract::PERMISSION_READ,
0294     $rawData = null
0295   ) {
0296       return $headers;
0297   }
0298 }