PageRenderTime 51ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/OpenId/Consumer.php

https://bitbucket.org/maatao/estrutura-b-sica-doctrine
PHP | 958 lines | 649 code | 74 blank | 235 comment | 185 complexity | 8bb4aa9bef51d6ffdcb0a1e5a0b0c06a MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_OpenId
  17. * @subpackage Zend_OpenId_Consumer
  18. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id: Consumer.php 23775 2011-03-01 17:25:24Z ralph $
  21. */
  22. /**
  23. * @see Zend_OpenId
  24. */
  25. require_once "Zend/OpenId.php";
  26. /**
  27. * @see Zend_OpenId_Extension
  28. */
  29. require_once "Zend/OpenId/Extension.php";
  30. /**
  31. * @see Zend_OpenId_Consumer_Storage
  32. */
  33. require_once "Zend/OpenId/Consumer/Storage.php";
  34. /**
  35. * @see Zend_Http_Client
  36. */
  37. require_once 'Zend/Http/Client.php';
  38. /**
  39. * OpenID consumer implementation
  40. *
  41. * @category Zend
  42. * @package Zend_OpenId
  43. * @subpackage Zend_OpenId_Consumer
  44. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  45. * @license http://framework.zend.com/license/new-bsd New BSD License
  46. */
  47. class Zend_OpenId_Consumer
  48. {
  49. /**
  50. * Reference to an implementation of storage object
  51. *
  52. * @var Zend_OpenId_Consumer_Storage $_storage
  53. */
  54. protected $_storage = null;
  55. /**
  56. * Enables or disables consumer to use association with server based on
  57. * Diffie-Hellman key agreement
  58. *
  59. * @var Zend_OpenId_Consumer_Storage $_dumbMode
  60. */
  61. protected $_dumbMode = false;
  62. /**
  63. * Internal cache to prevent unnecessary access to storage
  64. *
  65. * @var array $_cache
  66. */
  67. protected $_cache = array();
  68. /**
  69. * HTTP client to make HTTP requests
  70. *
  71. * @var Zend_Http_Client $_httpClient
  72. */
  73. private $_httpClient = null;
  74. /**
  75. * HTTP session to store climed_id between requests
  76. *
  77. * @var Zend_Session_Namespace $_session
  78. */
  79. private $_session = null;
  80. /**
  81. * Last error message for logi, check or verify failure
  82. *
  83. * @var string $_error
  84. */
  85. private $_error = '';
  86. /**
  87. * Constructs a Zend_OpenId_Consumer object with given $storage.
  88. * Enables or disables future association with server based on
  89. * Diffie-Hellman key agreement.
  90. *
  91. * @param Zend_OpenId_Consumer_Storage $storage implementation of custom
  92. * storage object
  93. * @param bool $dumbMode Enables or disables consumer to use association
  94. * with server based on Diffie-Hellman key agreement
  95. */
  96. public function __construct(Zend_OpenId_Consumer_Storage $storage = null,
  97. $dumbMode = false)
  98. {
  99. if ($storage === null) {
  100. require_once "Zend/OpenId/Consumer/Storage/File.php";
  101. $this->_storage = new Zend_OpenId_Consumer_Storage_File();
  102. } else {
  103. $this->_storage = $storage;
  104. }
  105. $this->_dumbMode = $dumbMode;
  106. }
  107. /**
  108. * Performs check (with possible user interaction) of OpenID identity.
  109. *
  110. * This is the first step of OpenID authentication process.
  111. * On success the function does not return (it does HTTP redirection to
  112. * server and exits). On failure it returns false.
  113. *
  114. * @param string $id OpenID identity
  115. * @param string $returnTo URL to redirect response from server to
  116. * @param string $root HTTP URL to identify consumer on server
  117. * @param mixed $extensions extension object or array of extensions objects
  118. * @param Zend_Controller_Response_Abstract $response an optional response
  119. * object to perform HTTP or HTML form redirection
  120. * @return bool
  121. */
  122. public function login($id, $returnTo = null, $root = null, $extensions = null,
  123. Zend_Controller_Response_Abstract $response = null)
  124. {
  125. return $this->_checkId(
  126. false,
  127. $id,
  128. $returnTo,
  129. $root,
  130. $extensions,
  131. $response);
  132. }
  133. /**
  134. * Performs immediate check (without user interaction) of OpenID identity.
  135. *
  136. * This is the first step of OpenID authentication process.
  137. * On success the function does not return (it does HTTP redirection to
  138. * server and exits). On failure it returns false.
  139. *
  140. * @param string $id OpenID identity
  141. * @param string $returnTo HTTP URL to redirect response from server to
  142. * @param string $root HTTP URL to identify consumer on server
  143. * @param mixed $extensions extension object or array of extensions objects
  144. * @param Zend_Controller_Response_Abstract $response an optional response
  145. * object to perform HTTP or HTML form redirection
  146. * @return bool
  147. */
  148. public function check($id, $returnTo=null, $root=null, $extensions = null,
  149. Zend_Controller_Response_Abstract $response = null)
  150. {
  151. return $this->_checkId(
  152. true,
  153. $id,
  154. $returnTo,
  155. $root,
  156. $extensions,
  157. $response);
  158. }
  159. /**
  160. * Verifies authentication response from OpenID server.
  161. *
  162. * This is the second step of OpenID authentication process.
  163. * The function returns true on successful authentication and false on
  164. * failure.
  165. *
  166. * @param array $params HTTP query data from OpenID server
  167. * @param string &$identity this argument is set to end-user's claimed
  168. * identifier or OpenID provider local identifier.
  169. * @param mixed $extensions extension object or array of extensions objects
  170. * @return bool
  171. */
  172. public function verify($params, &$identity = "", $extensions = null)
  173. {
  174. $this->_setError('');
  175. $version = 1.1;
  176. if (isset($params['openid_ns']) &&
  177. $params['openid_ns'] == Zend_OpenId::NS_2_0) {
  178. $version = 2.0;
  179. }
  180. if (isset($params["openid_claimed_id"])) {
  181. $identity = $params["openid_claimed_id"];
  182. } else if (isset($params["openid_identity"])){
  183. $identity = $params["openid_identity"];
  184. } else {
  185. $identity = "";
  186. }
  187. if ($version < 2.0 && !isset($params["openid_claimed_id"])) {
  188. if ($this->_session !== null) {
  189. if ($this->_session->identity === $identity) {
  190. $identity = $this->_session->claimed_id;
  191. }
  192. } else if (defined('SID')) {
  193. if (isset($_SESSION["zend_openid"]["identity"]) &&
  194. isset($_SESSION["zend_openid"]["claimed_id"]) &&
  195. $_SESSION["zend_openid"]["identity"] === $identity) {
  196. $identity = $_SESSION["zend_openid"]["claimed_id"];
  197. }
  198. } else {
  199. require_once "Zend/Session/Namespace.php";
  200. $this->_session = new Zend_Session_Namespace("zend_openid");
  201. if ($this->_session->identity === $identity) {
  202. $identity = $this->_session->claimed_id;
  203. }
  204. }
  205. }
  206. if (empty($params['openid_mode'])) {
  207. $this->_setError("Missing openid.mode");
  208. return false;
  209. }
  210. if (empty($params['openid_return_to'])) {
  211. $this->_setError("Missing openid.return_to");
  212. return false;
  213. }
  214. if (empty($params['openid_signed'])) {
  215. $this->_setError("Missing openid.signed");
  216. return false;
  217. }
  218. if (empty($params['openid_sig'])) {
  219. $this->_setError("Missing openid.sig");
  220. return false;
  221. }
  222. if ($params['openid_mode'] != 'id_res') {
  223. $this->_setError("Wrong openid.mode '".$params['openid_mode']."' != 'id_res'");
  224. return false;
  225. }
  226. if (empty($params['openid_assoc_handle'])) {
  227. $this->_setError("Missing openid.assoc_handle");
  228. return false;
  229. }
  230. if ($params['openid_return_to'] != Zend_OpenId::selfUrl()) {
  231. /* Ignore query part in openid.return_to */
  232. $pos = strpos($params['openid_return_to'], '?');
  233. if ($pos === false ||
  234. SUBSTR($params['openid_return_to'], 0 , $pos) != Zend_OpenId::selfUrl()) {
  235. $this->_setError("Wrong openid.return_to '".
  236. $params['openid_return_to']."' != '" . Zend_OpenId::selfUrl() ."'");
  237. return false;
  238. }
  239. }
  240. if ($version >= 2.0) {
  241. if (empty($params['openid_response_nonce'])) {
  242. $this->_setError("Missing openid.response_nonce");
  243. return false;
  244. }
  245. if (empty($params['openid_op_endpoint'])) {
  246. $this->_setError("Missing openid.op_endpoint");
  247. return false;
  248. /* OpenID 2.0 (11.3) Checking the Nonce */
  249. } else if (!$this->_storage->isUniqueNonce($params['openid_op_endpoint'], $params['openid_response_nonce'])) {
  250. $this->_setError("Duplicate openid.response_nonce");
  251. return false;
  252. }
  253. }
  254. if (!empty($params['openid_invalidate_handle'])) {
  255. if ($this->_storage->getAssociationByHandle(
  256. $params['openid_invalidate_handle'],
  257. $url,
  258. $macFunc,
  259. $secret,
  260. $expires)) {
  261. $this->_storage->delAssociation($url);
  262. }
  263. }
  264. if ($this->_storage->getAssociationByHandle(
  265. $params['openid_assoc_handle'],
  266. $url,
  267. $macFunc,
  268. $secret,
  269. $expires)) {
  270. $signed = explode(',', $params['openid_signed']);
  271. $data = '';
  272. foreach ($signed as $key) {
  273. $data .= $key . ':' . $params['openid_' . strtr($key,'.','_')] . "\n";
  274. }
  275. if (base64_decode($params['openid_sig']) ==
  276. Zend_OpenId::hashHmac($macFunc, $data, $secret)) {
  277. if (!Zend_OpenId_Extension::forAll($extensions, 'parseResponse', $params)) {
  278. $this->_setError("Extension::parseResponse failure");
  279. return false;
  280. }
  281. /* OpenID 2.0 (11.2) Verifying Discovered Information */
  282. if (isset($params['openid_claimed_id'])) {
  283. $id = $params['openid_claimed_id'];
  284. if (!Zend_OpenId::normalize($id)) {
  285. $this->_setError("Normalization failed");
  286. return false;
  287. } else if (!$this->_discovery($id, $discovered_server, $discovered_version)) {
  288. $this->_setError("Discovery failed: " . $this->getError());
  289. return false;
  290. } else if ((!empty($params['openid_identity']) &&
  291. $params["openid_identity"] != $id) ||
  292. (!empty($params['openid_op_endpoint']) &&
  293. $params['openid_op_endpoint'] != $discovered_server) ||
  294. $discovered_version != $version) {
  295. $this->_setError("Discovery information verification failed");
  296. return false;
  297. }
  298. }
  299. return true;
  300. }
  301. $this->_storage->delAssociation($url);
  302. $this->_setError("Signature check failed");
  303. return false;
  304. }
  305. else
  306. {
  307. /* Use dumb mode */
  308. if (isset($params['openid_claimed_id'])) {
  309. $id = $params['openid_claimed_id'];
  310. } else if (isset($params['openid_identity'])) {
  311. $id = $params['openid_identity'];
  312. } else {
  313. $this->_setError("Missing openid.claimed_id and openid.identity");
  314. return false;
  315. }
  316. if (!Zend_OpenId::normalize($id)) {
  317. $this->_setError("Normalization failed");
  318. return false;
  319. } else if (!$this->_discovery($id, $server, $discovered_version)) {
  320. $this->_setError("Discovery failed: " . $this->getError());
  321. return false;
  322. }
  323. /* OpenID 2.0 (11.2) Verifying Discovered Information */
  324. if ((isset($params['openid_identity']) &&
  325. $params["openid_identity"] != $id) ||
  326. (isset($params['openid_op_endpoint']) &&
  327. $params['openid_op_endpoint'] != $server) ||
  328. $discovered_version != $version) {
  329. $this->_setError("Discovery information verification failed");
  330. return false;
  331. }
  332. $params2 = array();
  333. foreach ($params as $key => $val) {
  334. if (strpos($key, 'openid_ns_') === 0) {
  335. $key = 'openid.ns.' . substr($key, strlen('openid_ns_'));
  336. } else if (strpos($key, 'openid_sreg_') === 0) {
  337. $key = 'openid.sreg.' . substr($key, strlen('openid_sreg_'));
  338. } else if (strpos($key, 'openid_') === 0) {
  339. $key = 'openid.' . substr($key, strlen('openid_'));
  340. }
  341. $params2[$key] = $val;
  342. }
  343. $params2['openid.mode'] = 'check_authentication';
  344. $ret = $this->_httpRequest($server, 'POST', $params2, $status);
  345. if ($status != 200) {
  346. $this->_setError("'Dumb' signature verification HTTP request failed");
  347. return false;
  348. }
  349. $r = array();
  350. if (is_string($ret)) {
  351. foreach(explode("\n", $ret) as $line) {
  352. $line = trim($line);
  353. if (!empty($line)) {
  354. $x = explode(':', $line, 2);
  355. if (is_array($x) && count($x) == 2) {
  356. list($key, $value) = $x;
  357. $r[trim($key)] = trim($value);
  358. }
  359. }
  360. }
  361. }
  362. $ret = $r;
  363. if (!empty($ret['invalidate_handle'])) {
  364. if ($this->_storage->getAssociationByHandle(
  365. $ret['invalidate_handle'],
  366. $url,
  367. $macFunc,
  368. $secret,
  369. $expires)) {
  370. $this->_storage->delAssociation($url);
  371. }
  372. }
  373. if (isset($ret['is_valid']) && $ret['is_valid'] == 'true') {
  374. if (!Zend_OpenId_Extension::forAll($extensions, 'parseResponse', $params)) {
  375. $this->_setError("Extension::parseResponse failure");
  376. return false;
  377. }
  378. return true;
  379. }
  380. $this->_setError("'Dumb' signature verification failed");
  381. return false;
  382. }
  383. }
  384. /**
  385. * Store assiciation in internal chace and external storage
  386. *
  387. * @param string $url OpenID server url
  388. * @param string $handle association handle
  389. * @param string $macFunc HMAC function (sha1 or sha256)
  390. * @param string $secret shared secret
  391. * @param integer $expires expiration UNIX time
  392. * @return void
  393. */
  394. protected function _addAssociation($url, $handle, $macFunc, $secret, $expires)
  395. {
  396. $this->_cache[$url] = array($handle, $macFunc, $secret, $expires);
  397. return $this->_storage->addAssociation(
  398. $url,
  399. $handle,
  400. $macFunc,
  401. $secret,
  402. $expires);
  403. }
  404. /**
  405. * Retrive assiciation information for given $url from internal cahce or
  406. * external storage
  407. *
  408. * @param string $url OpenID server url
  409. * @param string &$handle association handle
  410. * @param string &$macFunc HMAC function (sha1 or sha256)
  411. * @param string &$secret shared secret
  412. * @param integer &$expires expiration UNIX time
  413. * @return void
  414. */
  415. protected function _getAssociation($url, &$handle, &$macFunc, &$secret, &$expires)
  416. {
  417. if (isset($this->_cache[$url])) {
  418. $handle = $this->_cache[$url][0];
  419. $macFunc = $this->_cache[$url][1];
  420. $secret = $this->_cache[$url][2];
  421. $expires = $this->_cache[$url][3];
  422. return true;
  423. }
  424. if ($this->_storage->getAssociation(
  425. $url,
  426. $handle,
  427. $macFunc,
  428. $secret,
  429. $expires)) {
  430. $this->_cache[$url] = array($handle, $macFunc, $secret, $expires);
  431. return true;
  432. }
  433. return false;
  434. }
  435. /**
  436. * Performs HTTP request to given $url using given HTTP $method.
  437. * Send additinal query specified by variable/value array,
  438. * On success returns HTTP response without headers, false on failure.
  439. *
  440. * @param string $url OpenID server url
  441. * @param string $method HTTP request method 'GET' or 'POST'
  442. * @param array $params additional qwery parameters to be passed with
  443. * @param int &$staus HTTP status code
  444. * request
  445. * @return mixed
  446. */
  447. protected function _httpRequest($url, $method = 'GET', array $params = array(), &$status = null)
  448. {
  449. $client = $this->_httpClient;
  450. if ($client === null) {
  451. $client = new Zend_Http_Client(
  452. $url,
  453. array(
  454. 'maxredirects' => 4,
  455. 'timeout' => 15,
  456. 'useragent' => 'Zend_OpenId'
  457. )
  458. );
  459. } else {
  460. $client->setUri($url);
  461. }
  462. $client->resetParameters();
  463. if ($method == 'POST') {
  464. $client->setMethod(Zend_Http_Client::POST);
  465. $client->setParameterPost($params);
  466. } else {
  467. $client->setMethod(Zend_Http_Client::GET);
  468. $client->setParameterGet($params);
  469. }
  470. try {
  471. $response = $client->request();
  472. } catch (Exception $e) {
  473. $this->_setError('HTTP Request failed: ' . $e->getMessage());
  474. return false;
  475. }
  476. $status = $response->getStatus();
  477. $body = $response->getBody();
  478. if ($status == 200 || ($status == 400 && !empty($body))) {
  479. return $body;
  480. }else{
  481. $this->_setError('Bad HTTP response');
  482. return false;
  483. }
  484. }
  485. /**
  486. * Create (or reuse existing) association between OpenID consumer and
  487. * OpenID server based on Diffie-Hellman key agreement. Returns true
  488. * on success and false on failure.
  489. *
  490. * @param string $url OpenID server url
  491. * @param float $version OpenID protocol version
  492. * @param string $priv_key for testing only
  493. * @return bool
  494. */
  495. protected function _associate($url, $version, $priv_key=null)
  496. {
  497. /* Check if we already have association in chace or storage */
  498. if ($this->_getAssociation(
  499. $url,
  500. $handle,
  501. $macFunc,
  502. $secret,
  503. $expires)) {
  504. return true;
  505. }
  506. if ($this->_dumbMode) {
  507. /* Use dumb mode */
  508. return true;
  509. }
  510. $params = array();
  511. if ($version >= 2.0) {
  512. $params = array(
  513. 'openid.ns' => Zend_OpenId::NS_2_0,
  514. 'openid.mode' => 'associate',
  515. 'openid.assoc_type' => 'HMAC-SHA256',
  516. 'openid.session_type' => 'DH-SHA256',
  517. );
  518. } else {
  519. $params = array(
  520. 'openid.mode' => 'associate',
  521. 'openid.assoc_type' => 'HMAC-SHA1',
  522. 'openid.session_type' => 'DH-SHA1',
  523. );
  524. }
  525. $dh = Zend_OpenId::createDhKey(pack('H*', Zend_OpenId::DH_P),
  526. pack('H*', Zend_OpenId::DH_G),
  527. $priv_key);
  528. $dh_details = Zend_OpenId::getDhKeyDetails($dh);
  529. $params['openid.dh_modulus'] = base64_encode(
  530. Zend_OpenId::btwoc($dh_details['p']));
  531. $params['openid.dh_gen'] = base64_encode(
  532. Zend_OpenId::btwoc($dh_details['g']));
  533. $params['openid.dh_consumer_public'] = base64_encode(
  534. Zend_OpenId::btwoc($dh_details['pub_key']));
  535. while(1) {
  536. $ret = $this->_httpRequest($url, 'POST', $params, $status);
  537. if ($ret === false) {
  538. $this->_setError("HTTP request failed");
  539. return false;
  540. }
  541. $r = array();
  542. $bad_response = false;
  543. foreach(explode("\n", $ret) as $line) {
  544. $line = trim($line);
  545. if (!empty($line)) {
  546. $x = explode(':', $line, 2);
  547. if (is_array($x) && count($x) == 2) {
  548. list($key, $value) = $x;
  549. $r[trim($key)] = trim($value);
  550. } else {
  551. $bad_response = true;
  552. }
  553. }
  554. }
  555. if ($bad_response && strpos($ret, 'Unknown session type') !== false) {
  556. $r['error_code'] = 'unsupported-type';
  557. }
  558. $ret = $r;
  559. if (isset($ret['error_code']) &&
  560. $ret['error_code'] == 'unsupported-type') {
  561. if ($params['openid.session_type'] == 'DH-SHA256') {
  562. $params['openid.session_type'] = 'DH-SHA1';
  563. $params['openid.assoc_type'] = 'HMAC-SHA1';
  564. } else if ($params['openid.session_type'] == 'DH-SHA1') {
  565. $params['openid.session_type'] = 'no-encryption';
  566. } else {
  567. $this->_setError("The OpenID service responded with: " . $ret['error_code']);
  568. return false;
  569. }
  570. } else {
  571. break;
  572. }
  573. }
  574. if ($status != 200) {
  575. $this->_setError("The server responded with status code: " . $status);
  576. return false;
  577. }
  578. if ($version >= 2.0 &&
  579. isset($ret['ns']) &&
  580. $ret['ns'] != Zend_OpenId::NS_2_0) {
  581. $this->_setError("Wrong namespace definition in the server response");
  582. return false;
  583. }
  584. if (!isset($ret['assoc_handle']) ||
  585. !isset($ret['expires_in']) ||
  586. !isset($ret['assoc_type']) ||
  587. $params['openid.assoc_type'] != $ret['assoc_type']) {
  588. if ($params['openid.assoc_type'] != $ret['assoc_type']) {
  589. $this->_setError("The returned assoc_type differed from the supplied openid.assoc_type");
  590. } else {
  591. $this->_setError("Missing required data from provider (assoc_handle, expires_in, assoc_type are required)");
  592. }
  593. return false;
  594. }
  595. $handle = $ret['assoc_handle'];
  596. $expiresIn = $ret['expires_in'];
  597. if ($ret['assoc_type'] == 'HMAC-SHA1') {
  598. $macFunc = 'sha1';
  599. } else if ($ret['assoc_type'] == 'HMAC-SHA256' &&
  600. $version >= 2.0) {
  601. $macFunc = 'sha256';
  602. } else {
  603. $this->_setError("Unsupported assoc_type");
  604. return false;
  605. }
  606. if ((empty($ret['session_type']) ||
  607. ($version >= 2.0 && $ret['session_type'] == 'no-encryption')) &&
  608. isset($ret['mac_key'])) {
  609. $secret = base64_decode($ret['mac_key']);
  610. } else if (isset($ret['session_type']) &&
  611. $ret['session_type'] == 'DH-SHA1' &&
  612. !empty($ret['dh_server_public']) &&
  613. !empty($ret['enc_mac_key'])) {
  614. $dhFunc = 'sha1';
  615. } else if (isset($ret['session_type']) &&
  616. $ret['session_type'] == 'DH-SHA256' &&
  617. $version >= 2.0 &&
  618. !empty($ret['dh_server_public']) &&
  619. !empty($ret['enc_mac_key'])) {
  620. $dhFunc = 'sha256';
  621. } else {
  622. $this->_setError("Unsupported session_type");
  623. return false;
  624. }
  625. if (isset($dhFunc)) {
  626. $serverPub = base64_decode($ret['dh_server_public']);
  627. $dhSec = Zend_OpenId::computeDhSecret($serverPub, $dh);
  628. if ($dhSec === false) {
  629. $this->_setError("DH secret comutation failed");
  630. return false;
  631. }
  632. $sec = Zend_OpenId::digest($dhFunc, $dhSec);
  633. if ($sec === false) {
  634. $this->_setError("Could not create digest");
  635. return false;
  636. }
  637. $secret = $sec ^ base64_decode($ret['enc_mac_key']);
  638. }
  639. if ($macFunc == 'sha1') {
  640. if (Zend_OpenId::strlen($secret) != 20) {
  641. $this->_setError("The length of the sha1 secret must be 20");
  642. return false;
  643. }
  644. } else if ($macFunc == 'sha256') {
  645. if (Zend_OpenId::strlen($secret) != 32) {
  646. $this->_setError("The length of the sha256 secret must be 32");
  647. return false;
  648. }
  649. }
  650. $this->_addAssociation(
  651. $url,
  652. $handle,
  653. $macFunc,
  654. $secret,
  655. time() + $expiresIn);
  656. return true;
  657. }
  658. /**
  659. * Performs discovery of identity and finds OpenID URL, OpenID server URL
  660. * and OpenID protocol version. Returns true on succees and false on
  661. * failure.
  662. *
  663. * @param string &$id OpenID identity URL
  664. * @param string &$server OpenID server URL
  665. * @param float &$version OpenID protocol version
  666. * @return bool
  667. * @todo OpenID 2.0 (7.3) XRI and Yadis discovery
  668. */
  669. protected function _discovery(&$id, &$server, &$version)
  670. {
  671. $realId = $id;
  672. if ($this->_storage->getDiscoveryInfo(
  673. $id,
  674. $realId,
  675. $server,
  676. $version,
  677. $expire)) {
  678. $id = $realId;
  679. return true;
  680. }
  681. /* TODO: OpenID 2.0 (7.3) XRI and Yadis discovery */
  682. /* HTML-based discovery */
  683. $response = $this->_httpRequest($id, 'GET', array(), $status);
  684. if ($status != 200 || !is_string($response)) {
  685. return false;
  686. }
  687. if (preg_match(
  688. '/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.provider[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',
  689. $response,
  690. $r)) {
  691. $version = 2.0;
  692. $server = $r[3];
  693. } else if (preg_match(
  694. '/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.provider[ \t]*[^"\']*\\3[^>]*\/?>/i',
  695. $response,
  696. $r)) {
  697. $version = 2.0;
  698. $server = $r[2];
  699. } else if (preg_match(
  700. '/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.server[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',
  701. $response,
  702. $r)) {
  703. $version = 1.1;
  704. $server = $r[3];
  705. } else if (preg_match(
  706. '/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.server[ \t]*[^"\']*\\3[^>]*\/?>/i',
  707. $response,
  708. $r)) {
  709. $version = 1.1;
  710. $server = $r[2];
  711. } else {
  712. return false;
  713. }
  714. if ($version >= 2.0) {
  715. if (preg_match(
  716. '/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.local_id[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',
  717. $response,
  718. $r)) {
  719. $realId = $r[3];
  720. } else if (preg_match(
  721. '/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid2.local_id[ \t]*[^"\']*\\3[^>]*\/?>/i',
  722. $response,
  723. $r)) {
  724. $realId = $r[2];
  725. }
  726. } else {
  727. if (preg_match(
  728. '/<link[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.delegate[ \t]*[^"\']*\\1[^>]*href=(["\'])([^"\']+)\\2[^>]*\/?>/i',
  729. $response,
  730. $r)) {
  731. $realId = $r[3];
  732. } else if (preg_match(
  733. '/<link[^>]*href=(["\'])([^"\']+)\\1[^>]*rel=(["\'])[ \t]*(?:[^ \t"\']+[ \t]+)*?openid.delegate[ \t]*[^"\']*\\3[^>]*\/?>/i',
  734. $response,
  735. $r)) {
  736. $realId = $r[2];
  737. }
  738. }
  739. $expire = time() + 60 * 60;
  740. $this->_storage->addDiscoveryInfo($id, $realId, $server, $version, $expire);
  741. $id = $realId;
  742. return true;
  743. }
  744. /**
  745. * Performs check of OpenID identity.
  746. *
  747. * This is the first step of OpenID authentication process.
  748. * On success the function does not return (it does HTTP redirection to
  749. * server and exits). On failure it returns false.
  750. *
  751. * @param bool $immediate enables or disables interaction with user
  752. * @param string $id OpenID identity
  753. * @param string $returnTo HTTP URL to redirect response from server to
  754. * @param string $root HTTP URL to identify consumer on server
  755. * @param mixed $extensions extension object or array of extensions objects
  756. * @param Zend_Controller_Response_Abstract $response an optional response
  757. * object to perform HTTP or HTML form redirection
  758. * @return bool
  759. */
  760. protected function _checkId($immediate, $id, $returnTo=null, $root=null,
  761. $extensions=null, Zend_Controller_Response_Abstract $response = null)
  762. {
  763. $this->_setError('');
  764. if (!Zend_OpenId::normalize($id)) {
  765. $this->_setError("Normalisation failed");
  766. return false;
  767. }
  768. $claimedId = $id;
  769. if (!$this->_discovery($id, $server, $version)) {
  770. $this->_setError("Discovery failed: " . $this->getError());
  771. return false;
  772. }
  773. if (!$this->_associate($server, $version)) {
  774. $this->_setError("Association failed: " . $this->getError());
  775. return false;
  776. }
  777. if (!$this->_getAssociation(
  778. $server,
  779. $handle,
  780. $macFunc,
  781. $secret,
  782. $expires)) {
  783. /* Use dumb mode */
  784. unset($handle);
  785. unset($macFunc);
  786. unset($secret);
  787. unset($expires);
  788. }
  789. $params = array();
  790. if ($version >= 2.0) {
  791. $params['openid.ns'] = Zend_OpenId::NS_2_0;
  792. }
  793. $params['openid.mode'] = $immediate ?
  794. 'checkid_immediate' : 'checkid_setup';
  795. $params['openid.identity'] = $id;
  796. $params['openid.claimed_id'] = $claimedId;
  797. if ($version <= 2.0) {
  798. if ($this->_session !== null) {
  799. $this->_session->identity = $id;
  800. $this->_session->claimed_id = $claimedId;
  801. } else if (defined('SID')) {
  802. $_SESSION["zend_openid"] = array(
  803. "identity" => $id,
  804. "claimed_id" => $claimedId);
  805. } else {
  806. require_once "Zend/Session/Namespace.php";
  807. $this->_session = new Zend_Session_Namespace("zend_openid");
  808. $this->_session->identity = $id;
  809. $this->_session->claimed_id = $claimedId;
  810. }
  811. }
  812. if (isset($handle)) {
  813. $params['openid.assoc_handle'] = $handle;
  814. }
  815. $params['openid.return_to'] = Zend_OpenId::absoluteUrl($returnTo);
  816. if (empty($root)) {
  817. $root = Zend_OpenId::selfUrl();
  818. if ($root[strlen($root)-1] != '/') {
  819. $root = dirname($root);
  820. }
  821. }
  822. if ($version >= 2.0) {
  823. $params['openid.realm'] = $root;
  824. } else {
  825. $params['openid.trust_root'] = $root;
  826. }
  827. if (!Zend_OpenId_Extension::forAll($extensions, 'prepareRequest', $params)) {
  828. $this->_setError("Extension::prepareRequest failure");
  829. return false;
  830. }
  831. Zend_OpenId::redirect($server, $params, $response);
  832. return true;
  833. }
  834. /**
  835. * Sets HTTP client object to make HTTP requests
  836. *
  837. * @param Zend_Http_Client $client HTTP client object to be used
  838. */
  839. public function setHttpClient($client) {
  840. $this->_httpClient = $client;
  841. }
  842. /**
  843. * Returns HTTP client object that will be used to make HTTP requests
  844. *
  845. * @return Zend_Http_Client
  846. */
  847. public function getHttpClient() {
  848. return $this->_httpClient;
  849. }
  850. /**
  851. * Sets session object to store climed_id
  852. *
  853. * @param Zend_Session_Namespace $session HTTP client object to be used
  854. */
  855. public function setSession(Zend_Session_Namespace $session) {
  856. $this->_session = $session;
  857. }
  858. /**
  859. * Returns session object that is used to store climed_id
  860. *
  861. * @return Zend_Session_Namespace
  862. */
  863. public function getSession() {
  864. return $this->_session;
  865. }
  866. /**
  867. * Saves error message
  868. *
  869. * @param string $message error message
  870. */
  871. protected function _setError($message)
  872. {
  873. $this->_error = $message;
  874. }
  875. /**
  876. * Returns error message that explains failure of login, check or verify
  877. *
  878. * @return string
  879. */
  880. public function getError()
  881. {
  882. return $this->_error;
  883. }
  884. }