Auth.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. <?php
  2. /**
  3. * Zm_Auth
  4. *
  5. * @author Yannick Lorenz <ylorenz@1g6.biz>
  6. * @author Fabrizio La Rosa <fabrizio.larosa@unime.it>
  7. * @version 2.2
  8. * @copyright Copyright (c) 2009, Yannick Lorenz
  9. * @copyright Copyright (c) 2012, Fabrizio La Rosa
  10. */
  11. /**
  12. * Zm_Auth class documentation
  13. */
  14. // utils.php contains a small collection of useful functions
  15. require_once ("utils.php");
  16. /**
  17. * Zm_Auth is a class which allows to connect to the Zimbra admin or user space via SOAP
  18. *
  19. * Use this class to connect and login to a Zimbra server
  20. *
  21. * Example:
  22. *
  23. * // either authenticate as admin:
  24. *
  25. * $auth = new Zm_Auth($zimbraServer, $zimbraAdminEmail, $zimbraAdminPassword, "admin");
  26. *
  27. * // or authenticate as user:
  28. *
  29. * $auth = new Zm_Auth($zimbraServer, $userEmail, $userPassword, "user");
  30. *
  31. * // then login
  32. *
  33. * $l = $auth->login();
  34. *
  35. * if(is_a($l, "Exception")) {
  36. *
  37. * echo "Error : cannot login to $zimbraServer\n";
  38. *
  39. * echo $l->getMessage()."\n";
  40. *
  41. * exit();
  42. *
  43. * }
  44. *
  45. */
  46. class Zm_Auth
  47. {
  48. /////////////////////
  49. // Class Variables //
  50. /////////////////////
  51. /**
  52. * $auth
  53. * @var Zm_Auth $auth soap authentication
  54. */
  55. private $client;
  56. private $soapHeader;
  57. private $params;
  58. private $authToken;
  59. private $context;
  60. private $retryAttempts;
  61. /**
  62. * Constructor
  63. * @param string $server server name (example: zimbra.yourdomain.com)
  64. * @param string $username admin/user account's username
  65. * @param string $password admin/user account's password
  66. * @param string $authas authenticate as admin or user (default admin)
  67. * @param int $attempts how many times we retry to invoke a soapCall (default 3)
  68. */
  69. function __construct($server, $username, $password, $authas="admin", $attempts=3)
  70. {
  71. if ($authas == "admin")
  72. {
  73. $location = "https://" . $server . ":7071/service/admin/soap/";
  74. $uri = "urn:zimbraAdmin";
  75. }
  76. if ($authas == "user")
  77. {
  78. $location = "https://" . $server . "/service/soap/";
  79. $uri = "urn:zimbraAccount";
  80. }
  81. $this->context = $authas;
  82. $this->client = new SoapClient(null,
  83. array(
  84. 'location' => $location,
  85. 'uri' => $uri,
  86. 'trace' => 1,
  87. 'exceptions' => 1,
  88. 'soap_version' => SOAP_1_2,
  89. 'style' => SOAP_RPC,
  90. 'use' => SOAP_LITERAL,
  91. )
  92. );
  93. $this->params = array (
  94. new SoapVar('<ns1:account by="name">' . $username . '</ns1:account>', XSD_ANYXML),
  95. new SoapParam($password, "password")
  96. );
  97. $this->retryAttempts = $attempts;
  98. }
  99. /**
  100. * @internal
  101. */
  102. function execSoapCall($request, $params = array(), $options = null)
  103. {
  104. $result = null;
  105. $soapHeader = $this->getSoapHeader();
  106. if ($options["retry"] === false)
  107. $retry = false;
  108. else
  109. $retry = true;
  110. unset($options["retry"]);
  111. $n = 0;
  112. while (true)
  113. {
  114. try
  115. {
  116. $soapRes = null;
  117. $this->client->__soapCall(
  118. $request,
  119. $params,
  120. $options,
  121. $soapHeader
  122. );
  123. $soapRes = $this->client->__getLastResponse();
  124. //$this->auth->setSoapHeader($soapRes['authToken']);
  125. $xml = new xml2Array();
  126. $result = $xml->parse($soapRes);
  127. //echo htmlentities($result);
  128. //A tester : $this->objLastResponse = simplexml_load_string($this->_getBodyContent($this->objLastResponseRaw));
  129. break;
  130. }
  131. catch (SoapFault $exception)
  132. {
  133. // if $retryAttempts>0 retry after a random time using exponential backoff
  134. // if 'retry' option is false (usually when checking account existence) retries just once
  135. $n++;
  136. if ($this->retryAttempts > 0 &&
  137. $n <= $this->retryAttempts && ($retry || $n == 1) ) {
  138. $minT = 1+$n*1000000/10;
  139. $maxT = pow(2, $n-1)*1000000;
  140. $waitT = rand($minT, $maxT);
  141. usleep($waitT);
  142. } else {
  143. // we must re-throw the exception here because this method is only called by the
  144. // Zm_Account, Zm_Domain, Zm_Server class methods with their own try ... catch
  145. throw($exception);
  146. break;
  147. }
  148. }
  149. }
  150. return $result;
  151. }
  152. /**
  153. * @internal
  154. */
  155. function getSoapHeader()
  156. {
  157. return $this->soapHeader;
  158. }
  159. /**
  160. * @internal
  161. */
  162. function setSoapHeader($authToken = null)
  163. {
  164. if(!$authToken)
  165. {
  166. $this->soapHeader = new SoapHeader('urn:zimbra','context');
  167. }
  168. else
  169. {
  170. $this->soapHeader = array(
  171. new SoapHeader(
  172. 'urn:zimbra',
  173. 'context',
  174. new SoapVar('<ns2:context><ns2:authToken>' . $authToken . '</ns2:authToken></ns2:context>', XSD_ANYXML)
  175. )
  176. );
  177. }
  178. }
  179. /**
  180. * @internal
  181. */
  182. function getClient()
  183. {
  184. return $this->client;
  185. }
  186. /**
  187. * getRetryAttempts
  188. * @return int attempts how many times we retry to invoke a soapCall
  189. */
  190. function getRetryAttempts()
  191. {
  192. return $this->retryAttempts;
  193. }
  194. /**
  195. * setRetryAttempts
  196. * @param int $attempts how many times we retry to invoke a soapCall
  197. the wait time between attempts is progressively increased using an exponential backoff algorithm
  198. */
  199. function setRetryAttempts($attempts)
  200. {
  201. if (!$attempts)
  202. $attempts = 0;
  203. $this->retryAttempts = $attempts;
  204. }
  205. /**
  206. * login
  207. *
  208. * Use this method to login to a Zimbra server after you create an instance of this class
  209. *
  210. * Login parameters must be specified when calling the constructor
  211. */
  212. function login()
  213. {
  214. $result = null;
  215. $n = 0;
  216. while (true)
  217. {
  218. try
  219. {
  220. $this->setSoapHeader();
  221. $result = $this->client->__soapCall("AuthRequest", $this->params, null, $this->getSoapHeader());
  222. //$result = $this->client->__getLastResponse();
  223. //print_var($result);
  224. // Save the soapHeader with token
  225. $this->setSoapHeader($result['authToken']);
  226. break;
  227. }
  228. catch (SoapFault $exception)
  229. {
  230. // if $retryAttempts>0 retry after a random time using exponential backoff
  231. // for user logins retries just once
  232. $n++;
  233. if ($this->retryAttempts > 0 &&
  234. $n <= $this->retryAttempts && ($this->context == "admin" || $n == 1) ) {
  235. $minT = 1+$n*1000000/10;
  236. $maxT = pow(2, $n-1)*1000000;
  237. $waitT = rand($minT, $maxT);
  238. // wait times are shorter on login
  239. $waitT = $waitT/5;
  240. usleep($waitT);
  241. } else {
  242. $result = $exception;
  243. break;
  244. }
  245. }
  246. }
  247. return $result;
  248. }
  249. }
  250. ?>