File indexing completed on 2025-03-02 05:29:48

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  * @subpackage Management
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 /**
0024  * @see Zend_Http_Client
0025  */
0026  // require_once 'Zend/Http/Client.php';
0027  
0028  /**
0029  * @see Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract
0030  */
0031  // require_once 'Zend/Service/WindowsAzure/RetryPolicy/RetryPolicyAbstract.php';
0032  
0033  /**
0034  * @see Zend_Service_SqlAzure_Management_ServerInstance
0035  */
0036  // require_once 'Zend/Service/SqlAzure/Management/ServerInstance.php';
0037  
0038  /**
0039  * @see Zend_Service_SqlAzure_Management_FirewallRuleInstance
0040  */
0041  // require_once 'Zend/Service/SqlAzure/Management/FirewallRuleInstance.php';
0042 
0043  /** @see Zend_Xml_Security */
0044  // require_once 'Zend/Xml/Security.php';
0045 
0046 /**
0047  * @category   Zend
0048  * @package    Zend_Service_SqlAzure
0049  * @subpackage Management
0050  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0051  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0052  */
0053 class Zend_Service_SqlAzure_Management_Client
0054 {
0055   /**
0056    * Management service URL
0057    */
0058   const URL_MANAGEMENT        = "https://management.database.windows.net:8443";
0059   
0060   /**
0061    * Operations
0062    */
0063   const OP_OPERATIONS                = "operations";
0064   const OP_SERVERS                   = "servers";
0065   const OP_FIREWALLRULES             = "firewallrules";
0066 
0067   /**
0068    * Current API version
0069    * 
0070    * @var string
0071    */
0072   protected $_apiVersion = '1.0';
0073   
0074   /**
0075    * Subscription ID
0076    *
0077    * @var string
0078    */
0079   protected $_subscriptionId = '';
0080   
0081   /**
0082    * Management certificate path (.PEM)
0083    *
0084    * @var string
0085    */
0086   protected $_certificatePath = '';
0087   
0088   /**
0089    * Management certificate passphrase
0090    *
0091    * @var string
0092    */
0093   protected $_certificatePassphrase = '';
0094   
0095   /**
0096    * Zend_Http_Client channel used for communication with REST services
0097    * 
0098    * @var Zend_Http_Client
0099    */
0100   protected $_httpClientChannel = null; 
0101 
0102   /**
0103    * Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract instance
0104    * 
0105    * @var Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract
0106    */
0107   protected $_retryPolicy = null;
0108   
0109   /**
0110    * Returns the last request ID
0111    * 
0112    * @var string
0113    */
0114   protected $_lastRequestId = null;
0115   
0116   /**
0117    * Creates a new Zend_Service_SqlAzure_Management instance
0118    * 
0119    * @param string $subscriptionId Subscription ID
0120    * @param string $certificatePath Management certificate path (.PEM)
0121    * @param string $certificatePassphrase Management certificate passphrase
0122      * @param Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract $retryPolicy Retry policy to use when making requests
0123    */
0124   public function __construct(
0125     $subscriptionId,
0126     $certificatePath,
0127     $certificatePassphrase,
0128     Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract $retryPolicy = null
0129   ) {
0130     $this->_subscriptionId = $subscriptionId;
0131     $this->_certificatePath = $certificatePath;
0132     $this->_certificatePassphrase = $certificatePassphrase;
0133     
0134     $this->_retryPolicy = $retryPolicy;
0135     if (is_null($this->_retryPolicy)) {
0136         $this->_retryPolicy = Zend_Service_WindowsAzure_RetryPolicy_RetryPolicyAbstract::noRetry();
0137     }
0138     
0139     // Setup default Zend_Http_Client channel
0140     $options = array(
0141         'adapter'       => 'Zend_Http_Client_Adapter_Socket',
0142         'ssltransport'  => 'ssl',
0143       'sslcert'       => $this->_certificatePath,
0144       'sslpassphrase' => $this->_certificatePassphrase,
0145       'sslusecontext' => true,
0146     );
0147     if (function_exists('curl_init')) {
0148       // Set cURL options if cURL is used afterwards
0149       $options['curloptions'] = array(
0150           CURLOPT_FOLLOWLOCATION => true,
0151           CURLOPT_TIMEOUT => 120,
0152       );
0153     }
0154     $this->_httpClientChannel = new Zend_Http_Client(null, $options);
0155   }
0156   
0157   /**
0158    * Set the HTTP client channel to use
0159    * 
0160    * @param Zend_Http_Client_Adapter_Interface|string $adapterInstance Adapter instance or adapter class name.
0161    */
0162   public function setHttpClientChannel($adapterInstance = 'Zend_Http_Client_Adapter_Socket')
0163   {
0164     $this->_httpClientChannel->setAdapter($adapterInstance);
0165   }
0166   
0167     /**
0168      * Retrieve HTTP client channel
0169      * 
0170      * @return Zend_Http_Client_Adapter_Interface
0171      */
0172     public function getHttpClientChannel()
0173     {
0174         return $this->_httpClientChannel;
0175     }
0176   
0177   /**
0178    * Returns the Windows Azure subscription ID
0179    * 
0180    * @return string
0181    */
0182   public function getSubscriptionId()
0183   {
0184     return $this->_subscriptionId;
0185   }
0186   
0187   /**
0188    * Returns the last request ID.
0189    * 
0190    * @return string
0191    */
0192   public function getLastRequestId()
0193   {
0194     return $this->_lastRequestId;
0195   }
0196   
0197   /**
0198    * Get base URL for creating requests
0199    *
0200    * @return string
0201    */
0202   public function getBaseUrl()
0203   {
0204     return self::URL_MANAGEMENT . '/' . $this->_subscriptionId;
0205   }
0206   
0207   /**
0208    * Perform request using Zend_Http_Client channel
0209    *
0210    * @param string $path Path
0211    * @param string $queryString Query string
0212    * @param string $httpVerb HTTP verb the request will use
0213    * @param array $headers x-ms headers to add
0214    * @param mixed $rawData Optional RAW HTTP data to be sent over the wire
0215    * @return Zend_Http_Response
0216    */
0217   protected function _performRequest(
0218     $path = '/',
0219     $queryString = '',
0220     $httpVerb = Zend_Http_Client::GET,
0221     $headers = array(),
0222     $rawData = null
0223   ) {
0224       // Clean path
0225     if (strpos($path, '/') !== 0) {
0226       $path = '/' . $path;
0227     }
0228       
0229     // Clean headers
0230     if (is_null($headers)) {
0231         $headers = array();
0232     }
0233     
0234     // Ensure cUrl will also work correctly:
0235     //  - disable Content-Type if required
0236     //  - disable Expect: 100 Continue
0237     if (!isset($headers["Content-Type"])) {
0238       $headers["Content-Type"] = '';
0239     }
0240     //$headers["Expect"] = '';
0241 
0242     // Add version header
0243     $headers['x-ms-version'] = $this->_apiVersion;
0244         
0245     // URL encoding
0246     $path           = self::urlencode($path);
0247     $queryString    = self::urlencode($queryString);
0248 
0249     // Generate URL and sign request
0250     $requestUrl     = $this->getBaseUrl() . $path . $queryString;
0251     $requestHeaders = $headers;
0252 
0253     // Prepare request 
0254     $this->_httpClientChannel->resetParameters(true);
0255     $this->_httpClientChannel->setUri($requestUrl);
0256     $this->_httpClientChannel->setHeaders($requestHeaders);
0257     $this->_httpClientChannel->setRawData($rawData);
0258 
0259     // Execute request
0260     $response = $this->_retryPolicy->execute(
0261         array($this->_httpClientChannel, 'request'),
0262         array($httpVerb)
0263     );
0264     
0265     // Store request id
0266     $this->_lastRequestId = $response->getHeader('x-ms-request-id');
0267     
0268     return $response;
0269   }
0270   
0271   /** 
0272    * Parse result from Zend_Http_Response
0273    *
0274    * @param Zend_Http_Response $response Response from HTTP call
0275    * @return object
0276    * @throws Zend_Service_WindowsAzure_Exception
0277    */
0278   protected function _parseResponse(Zend_Http_Response $response = null)
0279   {
0280     if (is_null($response)) {
0281       // require_once 'Zend/Service/SqlAzure/Exception.php';
0282       throw new Zend_Service_SqlAzure_Exception('Response should not be null.');
0283     }
0284     
0285         $xml = @Zend_Xml_Security::scan($response->getBody());
0286         
0287         if ($xml !== false) {
0288             // Fetch all namespaces 
0289             $namespaces = array_merge($xml->getNamespaces(true), $xml->getDocNamespaces(true)); 
0290             
0291             // Register all namespace prefixes
0292             foreach ($namespaces as $prefix => $ns) { 
0293                 if ($prefix != '') {
0294                     $xml->registerXPathNamespace($prefix, $ns);
0295                 } 
0296             } 
0297         }
0298         
0299         return $xml;
0300   }
0301   
0302   /**
0303    * URL encode function
0304    * 
0305    * @param  string $value Value to encode
0306    * @return string        Encoded value
0307    */
0308   public static function urlencode($value)
0309   {
0310       return str_replace(' ', '%20', $value);
0311   }
0312   
0313     /**
0314      * Builds a query string from an array of elements
0315      * 
0316      * @param array     Array of elements
0317      * @return string   Assembled query string
0318      */
0319     public static function createQueryStringFromArray($queryString)
0320     {
0321       return count($queryString) > 0 ? '?' . implode('&', $queryString) : '';
0322     }
0323     
0324   /**
0325    * Get error message from Zend_Http_Response
0326    *
0327    * @param Zend_Http_Response $response Repsonse
0328    * @param string $alternativeError Alternative error message
0329    * @return string
0330    */
0331   protected function _getErrorMessage(Zend_Http_Response $response, $alternativeError = 'Unknown error.')
0332   {
0333     $response = $this->_parseResponse($response);
0334     if ($response && $response->Message) {
0335       return (string)$response->Message;
0336     } else {
0337       return $alternativeError;
0338     }
0339   }
0340   
0341   /**
0342    * The Create Server operation adds a new SQL Azure server to a subscription.
0343    * 
0344    * @param string $administratorLogin Administrator login.
0345    * @param string $administratorPassword Administrator password.
0346    * @param string $location Location of the server.
0347    * @return Zend_Service_SqlAzure_Management_ServerInstance Server information.
0348    * @throws Zend_Service_SqlAzure_Management_Exception
0349    */
0350   public function createServer($administratorLogin, $administratorPassword, $location)
0351   {
0352     if ($administratorLogin == '' || is_null($administratorLogin)) {
0353                     // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0354                     throw new Zend_Service_SqlAzure_Management_Exception('Administrator login should be specified.');
0355                 }
0356     if ($administratorPassword == '' || is_null($administratorPassword)) {
0357                     // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0358                     throw new Zend_Service_SqlAzure_Management_Exception('Administrator password should be specified.');
0359                 }
0360                 if (is_null($location) && is_null($affinityGroup)) {
0361                     // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0362                     throw new Zend_Service_SqlAzure_Management_Exception('Please specify a location for the server.');
0363                 }
0364       
0365                 $response = $this->_performRequest(self::OP_SERVERS, '',
0366         Zend_Http_Client::POST,
0367         array('Content-Type' => 'application/xml; charset=utf-8'),
0368         '<Server xmlns="http://schemas.microsoft.com/sqlazure/2010/12/"><AdministratorLogin>' . $administratorLogin . '</AdministratorLogin><AdministratorLoginPassword>' . $administratorPassword . '</AdministratorLoginPassword><Location>' . $location . '</Location></Server>');
0369   
0370                 if ($response->isSuccessful()) {
0371       $xml = $this->_parseResponse($response);
0372       
0373       return new Zend_Service_SqlAzure_Management_ServerInstance(
0374         (string)$xml,
0375         $administratorLogin,
0376         $location
0377       );
0378                 } else {
0379       // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0380       throw new Zend_Service_SqlAzure_Management_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
0381     } 
0382   }
0383   
0384   /**
0385    * The Get Servers operation enumerates SQL Azure servers that are provisioned for a subscription.
0386    * 
0387    * @return array An array of Zend_Service_SqlAzure_Management_ServerInstance.
0388    * @throws Zend_Service_SqlAzure_Management_Exception
0389    */
0390   public function listServers()
0391   {
0392             $response = $this->_performRequest(self::OP_SERVERS);
0393   
0394             if ($response->isSuccessful()) {
0395     $xml = $this->_parseResponse($response);
0396     $xmlServices = null;
0397       
0398                 if (!$xml->Server) {
0399                     return array();
0400     }
0401     if (count($xml->Server) > 1) {
0402             $xmlServices = $xml->Server;
0403         } else {
0404             $xmlServices = array($xml->Server);
0405         }
0406         
0407     $services = array();
0408     if (!is_null($xmlServices)) {       
0409         
0410                     for ($i = 0; $i < count($xmlServices); $i++) {
0411                         $services[] = new Zend_Service_SqlAzure_Management_ServerInstance(
0412                                       (string)$xmlServices[$i]->Name,
0413               (string)$xmlServices[$i]->AdministratorLogin,
0414               (string)$xmlServices[$i]->Location
0415           );
0416                     }
0417     }
0418     return $services;
0419             } else {
0420     // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0421     throw new Zend_Service_SqlAzure_Management_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
0422             }
0423   }
0424   
0425   /**
0426    * The Drop Server operation drops a SQL Azure server from a subscription.
0427    * 
0428    * @param string $serverName Server to drop.
0429    * @throws Zend_Service_SqlAzure_Management_Exception
0430    */
0431   public function dropServer($serverName)
0432   {
0433             if ($serverName == '' || is_null($serverName)) {
0434                 // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0435                 throw new Zend_Service_SqlAzure_Management_Exception('Server name should be specified.');
0436             }
0437       
0438             $response = $this->_performRequest(self::OP_SERVERS . '/' . $serverName, '', Zend_Http_Client::DELETE);
0439 
0440             if (!$response->isSuccessful()) {
0441     // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0442     throw new Zend_Service_SqlAzure_Management_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
0443             } 
0444   }
0445   
0446   /**
0447    * The Set Server Administrator Password operation sets the administrative password of a SQL Azure server for a subscription.
0448    * 
0449    * @param string $serverName Server to set password for.
0450    * @param string $administratorPassword Administrator password.
0451    * @throws Zend_Service_SqlAzure_Management_Exception
0452    */
0453   public function setAdministratorPassword($serverName, $administratorPassword)
0454   {
0455             if ($serverName == '' || is_null($serverName)) {
0456     // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0457         throw new Zend_Service_SqlAzure_Management_Exception('Server name should be specified.');
0458             }
0459             if ($administratorPassword == '' || is_null($administratorPassword)) {
0460                 // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0461         throw new Zend_Service_SqlAzure_Management_Exception('Administrator password should be specified.');
0462             }
0463       
0464             $response = $this->_performRequest(self::OP_SERVERS . '/' . $serverName, '?op=ResetPassword',
0465         Zend_Http_Client::POST,
0466         array('Content-Type' => 'application/xml; charset=utf-8'),
0467         '<AdministratorLoginPassword xmlns="http://schemas.microsoft.com/sqlazure/2010/12/">' . $administratorPassword . '</AdministratorLoginPassword>');
0468         
0469             if (!$response->isSuccessful()) {
0470     // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0471     throw new Zend_Service_SqlAzure_Management_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
0472             } 
0473   }
0474   
0475   /**
0476    * The Set Server Firewall Rule operation updates an existing firewall rule or adds a new firewall rule for a SQL Azure server that belongs to a subscription.
0477    * 
0478    * @param string $serverName Server name.
0479    * @param string $ruleName Firewall rule name.
0480    * @param string $startIpAddress Start IP address.
0481    * @param string $endIpAddress End IP address.
0482    * @return Zend_Service_SqlAzure_Management_FirewallRuleInstance
0483    * @throws Zend_Service_SqlAzure_Management_Exception
0484    */
0485   public function createFirewallRule($serverName, $ruleName, $startIpAddress, $endIpAddress)
0486   {
0487             if ($serverName == '' || is_null($serverName)) {
0488                 // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0489                 throw new Zend_Service_SqlAzure_Management_Exception('Server name should be specified.');
0490             }
0491             if ($ruleName == '' || is_null($ruleName)) {
0492                 // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0493                 throw new Zend_Service_SqlAzure_Management_Exception('Rule name should be specified.');
0494             }
0495             if ($startIpAddress == '' || is_null($startIpAddress) || !filter_var($startIpAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
0496                 // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0497                 throw new Zend_Service_SqlAzure_Management_Exception('Start IP address should be specified.');
0498             }
0499             if ($endIpAddress == '' || is_null($endIpAddress) || !filter_var($endIpAddress, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
0500                 // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0501                 throw new Zend_Service_SqlAzure_Management_Exception('End IP address should be specified.');
0502             }
0503       
0504             $response = $this->_performRequest(self::OP_SERVERS . '/' . $serverName . '/' . self::OP_FIREWALLRULES . '/' . $ruleName, '',
0505         Zend_Http_Client::PUT,
0506         array('Content-Type' => 'application/xml; charset=utf-8'),
0507         '<FirewallRule xmlns="http://schemas.microsoft.com/sqlazure/2010/12/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.microsoft.com/sqlazure/2010/12/ FirewallRule.xsd"><StartIpAddress>' . $startIpAddress . '</StartIpAddress><EndIpAddress>' . $endIpAddress . '</EndIpAddress></FirewallRule>');
0508 
0509             if ($response->isSuccessful()) {
0510     
0511         return new Zend_Service_SqlAzure_Management_FirewallRuleInstance(
0512           $ruleName,
0513           $startIpAddress,
0514           $endIpAddress
0515         );
0516             } else {
0517     // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0518     throw new Zend_Service_SqlAzure_Management_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
0519             }
0520   }
0521   
0522   /**
0523    * The Get Server Firewall Rules operation retrieves a list of all the firewall rules for a SQL Azure server that belongs to a subscription.
0524    * 
0525    * @param string $serverName Server name.
0526    * @return Array of Zend_Service_SqlAzure_Management_FirewallRuleInstance.
0527    * @throws Zend_Service_SqlAzure_Management_Exception
0528    */
0529   public function listFirewallRules($serverName)
0530   {
0531             if ($serverName == '' || is_null($serverName)) {
0532     // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0533                 throw new Zend_Service_SqlAzure_Management_Exception('Server name should be specified.');
0534             }
0535       
0536       $response = $this->_performRequest(self::OP_SERVERS . '/' . $serverName . '/' . self::OP_FIREWALLRULES);
0537   
0538             if ($response->isSuccessful()) {
0539     $xml = $this->_parseResponse($response);
0540     $xmlServices = null;
0541       
0542         if (!$xml->FirewallRule) {
0543                     return array();
0544     }
0545     if (count($xml->FirewallRule) > 1) {
0546             $xmlServices = $xml->FirewallRule;
0547         } else {
0548             $xmlServices = array($xml->FirewallRule);
0549         }
0550         
0551     $services = array();
0552     if (!is_null($xmlServices)) {       
0553                     
0554                     for ($i = 0; $i < count($xmlServices); $i++) {
0555                         $services[] = new Zend_Service_SqlAzure_Management_FirewallRuleInstance(
0556               (string)$xmlServices[$i]->Name,
0557               (string)$xmlServices[$i]->StartIpAddress,
0558               (string)$xmlServices[$i]->EndIpAddress
0559           );
0560                     }
0561     }
0562     return $services;
0563             } else {
0564     // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0565     throw new Zend_Service_SqlAzure_Management_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
0566             }   
0567   }
0568   
0569   /**
0570    * The Delete Server Firewall Rule operation deletes a firewall rule from a SQL Azure server that belongs to a subscription.
0571    * 
0572    * @param string $serverName Server name.
0573    * @param string $ruleName Rule name.
0574    * @throws Zend_Service_SqlAzure_Management_Exception
0575    */
0576   public function deleteFirewallRule($serverName, $ruleName)
0577   {
0578     if ($serverName == '' || is_null($serverName)) {
0579       // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0580         throw new Zend_Service_SqlAzure_Management_Exception('Server name should be specified.');
0581       }
0582     if ($ruleName == '' || is_null($ruleName)) {
0583       // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0584         throw new Zend_Service_SqlAzure_Management_Exception('Rule name should be specified.');
0585       }
0586       
0587         $response = $this->_performRequest(self::OP_SERVERS . '/' . $serverName . '/' . self::OP_FIREWALLRULES . '/' . $ruleName, '',
0588         Zend_Http_Client::DELETE);
0589 
0590       if (!$response->isSuccessful()) {
0591       // require_once 'Zend/Service/SqlAzure/Management/Exception.php';
0592       throw new Zend_Service_SqlAzure_Management_Exception($this->_getErrorMessage($response, 'Resource could not be accessed.'));
0593     }
0594   }
0595   
0596   /**
0597    * Creates a firewall rule for Microsoft Services. This is required if access to SQL Azure is required from other services like Windows Azure.
0598    * 
0599    * @param string $serverName Server name.
0600    * @param boolean $allowAccess Allow access from other Microsoft Services?
0601    * @throws Zend_Service_SqlAzure_Management_Exception
0602    */
0603   public function createFirewallRuleForMicrosoftServices($serverName, $allowAccess)
0604   {
0605     if ($allowAccess) {
0606       $this->createFirewallRule($serverName, 'MicrosoftServices', '0.0.0.0', '0.0.0.0');
0607     } else {
0608       $this->deleteFirewallRule($serverName, 'MicrosoftServices');
0609     }
0610   }
0611   
0612 }