*prechod na novsiu verziu ZF
[sport-group.git] / library / Zend / OpenId / Provider.php
blob25342b3d51d68b44762ec6fa944ee7ae64b0699a
1 <?php
3 /**
4 * Zend Framework
6 * LICENSE
8 * This source file is subject to the new BSD license that is bundled
9 * with this package in the file LICENSE.txt.
10 * It is also available through the world-wide-web at this URL:
11 * http://framework.zend.com/license/new-bsd
12 * If you did not receive a copy of the license and are unable to
13 * obtain it through the world-wide-web, please send an email
14 * to license@zend.com so we can send you a copy immediately.
16 * @category Zend
17 * @package Zend_OpenId
18 * @subpackage Zend_OpenId_Provider
19 * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
20 * @license http://framework.zend.com/license/new-bsd New BSD License
21 * @version $Id: Provider.php 16212 2009-06-21 19:24:49Z thomas $
24 /**
25 * @see Zend_OpenId
27 require_once "Zend/OpenId.php";
29 /**
30 * @see Zend_OpenId_Extension
32 require_once "Zend/OpenId/Extension.php";
34 /**
35 * OpenID provider (server) implementation
37 * @category Zend
38 * @package Zend_OpenId
39 * @subpackage Zend_OpenId_Provider
40 * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
41 * @license http://framework.zend.com/license/new-bsd New BSD License
43 class Zend_OpenId_Provider
46 /**
47 * Reference to an implementation of storage object
49 * @var Zend_OpenId_Provider_Storage $_storage
51 private $_storage;
53 /**
54 * Reference to an implementation of user object
56 * @var Zend_OpenId_Provider_User $_user
58 private $_user;
60 /**
61 * Time to live of association session in secconds
63 * @var integer $_sessionTtl
65 private $_sessionTtl;
67 /**
68 * URL to peform interactive user login
70 * @var string $_loginUrl
72 private $_loginUrl;
74 /**
75 * URL to peform interactive validation of consumer by user
77 * @var string $_trustUrl
79 private $_trustUrl;
81 /**
82 * The OP Endpoint URL
84 * @var string $_opEndpoint
86 private $_opEndpoint;
88 /**
89 * Constructs a Zend_OpenId_Provider object with given parameters.
91 * @param string $loginUrl is an URL that provides login screen for
92 * end-user (by default it is the same URL with additional GET variable
93 * openid.action=login)
94 * @param string $trustUrl is an URL that shows a question if end-user
95 * trust to given consumer (by default it is the same URL with additional
96 * GET variable openid.action=trust)
97 * @param Zend_OpenId_Provider_User $user is an object for communication
98 * with User-Agent and store information about logged-in user (it is a
99 * Zend_OpenId_Provider_User_Session object by default)
100 * @param Zend_OpenId_Provider_Storage $storage is an object for keeping
101 * persistent database (it is a Zend_OpenId_Provider_Storage_File object
102 * by default)
103 * @param integer $sessionTtl is a default time to live for association
104 * session in seconds (1 hour by default). Consumer must reestablish
105 * association after that time.
107 public function __construct($loginUrl = null,
108 $trustUrl = null,
109 Zend_OpenId_Provider_User $user = null,
110 Zend_OpenId_Provider_Storage $storage = null,
111 $sessionTtl = 3600)
113 if ($loginUrl === null) {
114 $loginUrl = Zend_OpenId::selfUrl() . '?openid.action=login';
115 } else {
116 $loginUrl = Zend_OpenId::absoluteUrl($loginUrl);
118 $this->_loginUrl = $loginUrl;
119 if ($trustUrl === null) {
120 $trustUrl = Zend_OpenId::selfUrl() . '?openid.action=trust';
121 } else {
122 $trustUrl = Zend_OpenId::absoluteUrl($trustUrl);
124 $this->_trustUrl = $trustUrl;
125 if ($user === null) {
126 require_once "Zend/OpenId/Provider/User/Session.php";
127 $this->_user = new Zend_OpenId_Provider_User_Session();
128 } else {
129 $this->_user = $user;
131 if ($storage === null) {
132 require_once "Zend/OpenId/Provider/Storage/File.php";
133 $this->_storage = new Zend_OpenId_Provider_Storage_File();
134 } else {
135 $this->_storage = $storage;
137 $this->_sessionTtl = $sessionTtl;
141 * Sets the OP Endpoint URL
143 * @param string $url the OP Endpoint URL
144 * @return null
146 public function setOpEndpoint($url)
148 $this->_opEndpoint = $url;
152 * Registers a new user with given $id and $password
153 * Returns true in case of success and false if user with given $id already
154 * exists
156 * @param string $id user identity URL
157 * @param string $password encoded user password
158 * @return bool
160 public function register($id, $password)
162 if (!Zend_OpenId::normalize($id) || empty($id)) {
163 return false;
165 return $this->_storage->addUser($id, md5($id.$password));
169 * Returns true if user with given $id exists and false otherwise
171 * @param string $id user identity URL
172 * @return bool
174 public function hasUser($id) {
175 if (!Zend_OpenId::normalize($id)) {
176 return false;
178 return $this->_storage->hasUser($id);
182 * Performs login of user with given $id and $password
183 * Returns true in case of success and false otherwise
185 * @param string $id user identity URL
186 * @param string $password user password
187 * @return bool
189 public function login($id, $password)
191 if (!Zend_OpenId::normalize($id)) {
192 return false;
194 if (!$this->_storage->checkUser($id, md5($id.$password))) {
195 return false;
197 $this->_user->setLoggedInUser($id);
198 return true;
202 * Performs logout. Clears information about logged in user.
204 * @return void
206 public function logout()
208 $this->_user->delLoggedInUser();
209 return true;
213 * Returns identity URL of current logged in user or false
215 * @return mixed
217 public function getLoggedInUser() {
218 return $this->_user->getLoggedInUser();
222 * Retrieve consumer's root URL from request query.
223 * Returns URL or false in case of failure
225 * @param array $params query arguments
226 * @return mixed
228 public function getSiteRoot($params)
230 $version = 1.1;
231 if (isset($params['openid_ns']) &&
232 $params['openid_ns'] == Zend_OpenId::NS_2_0) {
233 $version = 2.0;
235 if ($version >= 2.0 && isset($params['openid_realm'])) {
236 $root = $params['openid_realm'];
237 } else if ($version < 2.0 && isset($params['openid_trust_root'])) {
238 $root = $params['openid_trust_root'];
239 } else if (isset($params['openid_return_to'])) {
240 $root = $params['openid_return_to'];
241 } else {
242 return false;
244 if (Zend_OpenId::normalizeUrl($root) && !empty($root)) {
245 return $root;
247 return false;
251 * Allows consumer with given root URL to authenticate current logged
252 * in user. Returns true on success and false on error.
254 * @param string $root root URL
255 * @param mixed $extensions extension object or array of extensions objects
256 * @return bool
258 public function allowSite($root, $extensions=null)
260 $id = $this->getLoggedInUser();
261 if ($id === false) {
262 return false;
264 if ($extensions !== null) {
265 $data = array();
266 Zend_OpenId_Extension::forAll($extensions, 'getTrustData', $data);
267 } else {
268 $data = true;
270 $this->_storage->addSite($id, $root, $data);
271 return true;
275 * Prohibit consumer with given root URL to authenticate current logged
276 * in user. Returns true on success and false on error.
278 * @param string $root root URL
279 * @return bool
281 public function denySite($root)
283 $id = $this->getLoggedInUser();
284 if ($id === false) {
285 return false;
287 $this->_storage->addSite($id, $root, false);
288 return true;
292 * Delete consumer with given root URL from known sites of current logged
293 * in user. Next time this consumer will try to authenticate the user,
294 * Provider will ask user's confirmation.
295 * Returns true on success and false on error.
297 * @param string $root root URL
298 * @return bool
300 public function delSite($root)
302 $id = $this->getLoggedInUser();
303 if ($id === false) {
304 return false;
306 $this->_storage->addSite($id, $root, null);
307 return true;
311 * Returns list of known consumers for current logged in user or false
312 * if he is not logged in.
314 * @return mixed
316 public function getTrustedSites()
318 $id = $this->getLoggedInUser();
319 if ($id === false) {
320 return false;
322 return $this->_storage->getTrustedSites($id);
326 * Handles HTTP request from consumer
328 * @param array $params GET or POST variables. If this parameter is omited
329 * or set to null, then $_GET or $_POST superglobal variable is used
330 * according to REQUEST_METHOD.
331 * @param mixed $extensions extension object or array of extensions objects
332 * @param Zend_Controller_Response_Abstract $response an optional response
333 * object to perform HTTP or HTML form redirection
334 * @return mixed
336 public function handle($params=null, $extensions=null,
337 Zend_Controller_Response_Abstract $response = null)
339 if ($params === null) {
340 if ($_SERVER["REQUEST_METHOD"] == "GET") {
341 $params = $_GET;
342 } else if ($_SERVER["REQUEST_METHOD"] == "POST") {
343 $params = $_POST;
344 } else {
345 return false;
348 $version = 1.1;
349 if (isset($params['openid_ns']) &&
350 $params['openid_ns'] == Zend_OpenId::NS_2_0) {
351 $version = 2.0;
353 if (isset($params['openid_mode'])) {
354 if ($params['openid_mode'] == 'associate') {
355 $response = $this->_associate($version, $params);
356 $ret = '';
357 foreach ($response as $key => $val) {
358 $ret .= $key . ':' . $val . "\n";
360 return $ret;
361 } else if ($params['openid_mode'] == 'checkid_immediate') {
362 $ret = $this->_checkId($version, $params, 1, $extensions, $response);
363 if (is_bool($ret)) return $ret;
364 if (!empty($params['openid_return_to'])) {
365 Zend_OpenId::redirect($params['openid_return_to'], $ret, $response);
367 return true;
368 } else if ($params['openid_mode'] == 'checkid_setup') {
369 $ret = $this->_checkId($version, $params, 0, $extensions, $response);
370 if (is_bool($ret)) return $ret;
371 if (!empty($params['openid_return_to'])) {
372 Zend_OpenId::redirect($params['openid_return_to'], $ret, $response);
374 return true;
375 } else if ($params['openid_mode'] == 'check_authentication') {
376 $response = $this->_checkAuthentication($version, $params);
377 $ret = '';
378 foreach ($response as $key => $val) {
379 $ret .= $key . ':' . $val . "\n";
381 return $ret;
384 return false;
388 * Generates a secret key for given hash function, returns RAW key or false
389 * if function is not supported
391 * @param string $func hash function (sha1 or sha256)
392 * @return mixed
394 protected function _genSecret($func)
396 if ($func == 'sha1') {
397 $macLen = 20; /* 160 bit */
398 } else if ($func == 'sha256') {
399 $macLen = 32; /* 256 bit */
400 } else {
401 return false;
403 return Zend_OpenId::randomBytes($macLen);
407 * Processes association request from OpenID consumerm generates secret
408 * shared key and send it back using Diffie-Hellman encruption.
409 * Returns array of variables to push back to consumer.
411 * @param float $version OpenID version
412 * @param array $params GET or POST request variables
413 * @return array
415 protected function _associate($version, $params)
417 $ret = array();
419 if ($version >= 2.0) {
420 $ret['ns'] = Zend_OpenId::NS_2_0;
423 if (isset($params['openid_assoc_type']) &&
424 $params['openid_assoc_type'] == 'HMAC-SHA1') {
425 $macFunc = 'sha1';
426 } else if (isset($params['openid_assoc_type']) &&
427 $params['openid_assoc_type'] == 'HMAC-SHA256' &&
428 $version >= 2.0) {
429 $macFunc = 'sha256';
430 } else {
431 $ret['error'] = 'Wrong "openid.assoc_type"';
432 $ret['error-code'] = 'unsupported-type';
433 return $ret;
436 $ret['assoc_type'] = $params['openid_assoc_type'];
438 $secret = $this->_genSecret($macFunc);
440 if (empty($params['openid_session_type']) ||
441 $params['openid_session_type'] == 'no-encryption') {
442 $ret['mac_key'] = base64_encode($secret);
443 } else if (isset($params['openid_session_type']) &&
444 $params['openid_session_type'] == 'DH-SHA1') {
445 $dhFunc = 'sha1';
446 } else if (isset($params['openid_session_type']) &&
447 $params['openid_session_type'] == 'DH-SHA256' &&
448 $version >= 2.0) {
449 $dhFunc = 'sha256';
450 } else {
451 $ret['error'] = 'Wrong "openid.session_type"';
452 $ret['error-code'] = 'unsupported-type';
453 return $ret;
456 if (isset($params['openid_session_type'])) {
457 $ret['session_type'] = $params['openid_session_type'];
460 if (isset($dhFunc)) {
461 if (empty($params['openid_dh_consumer_public'])) {
462 $ret['error'] = 'Wrong "openid.dh_consumer_public"';
463 return $ret;
465 if (empty($params['openid_dh_gen'])) {
466 $g = pack('H*', Zend_OpenId::DH_G);
467 } else {
468 $g = base64_decode($params['openid_dh_gen']);
470 if (empty($params['openid_dh_modulus'])) {
471 $p = pack('H*', Zend_OpenId::DH_P);
472 } else {
473 $p = base64_decode($params['openid_dh_modulus']);
476 $dh = Zend_OpenId::createDhKey($p, $g);
477 $dh_details = Zend_OpenId::getDhKeyDetails($dh);
479 $sec = Zend_OpenId::computeDhSecret(
480 base64_decode($params['openid_dh_consumer_public']), $dh);
481 if ($sec === false) {
482 $ret['error'] = 'Wrong "openid.session_type"';
483 $ret['error-code'] = 'unsupported-type';
484 return $ret;
486 $sec = Zend_OpenId::digest($dhFunc, $sec);
487 $ret['dh_server_public'] = base64_encode(
488 Zend_OpenId::btwoc($dh_details['pub_key']));
489 $ret['enc_mac_key'] = base64_encode($secret ^ $sec);
492 $handle = uniqid();
493 $expiresIn = $this->_sessionTtl;
495 $ret['assoc_handle'] = $handle;
496 $ret['expires_in'] = $expiresIn;
498 $this->_storage->addAssociation($handle,
499 $macFunc, $secret, time() + $expiresIn);
501 return $ret;
505 * Performs authentication (or authentication check).
507 * @param float $version OpenID version
508 * @param array $params GET or POST request variables
509 * @param bool $immediate enables or disables interaction with user
510 * @param mixed $extensions extension object or array of extensions objects
511 * @param Zend_Controller_Response_Abstract $response
512 * @return array
514 protected function _checkId($version, $params, $immediate, $extensions=null,
515 Zend_Controller_Response_Abstract $response = null)
517 $ret = array();
519 if ($version >= 2.0) {
520 $ret['openid.ns'] = Zend_OpenId::NS_2_0;
522 $root = $this->getSiteRoot($params);
523 if ($root === false) {
524 return false;
527 if (isset($params['openid_identity']) &&
528 !$this->_storage->hasUser($params['openid_identity'])) {
529 $ret['openid.mode'] = ($immediate && $version >= 2.0) ? 'setup_needed': 'cancel';
530 return $ret;
533 /* Check if user already logged in into the server */
534 if (!isset($params['openid_identity']) ||
535 $this->_user->getLoggedInUser() !== $params['openid_identity']) {
536 $params2 = array();
537 foreach ($params as $key => $val) {
538 if (strpos($key, 'openid_ns_') === 0) {
539 $key = 'openid.ns.' . substr($key, strlen('openid_ns_'));
540 } else if (strpos($key, 'openid_sreg_') === 0) {
541 $key = 'openid.sreg.' . substr($key, strlen('openid_sreg_'));
542 } else if (strpos($key, 'openid_') === 0) {
543 $key = 'openid.' . substr($key, strlen('openid_'));
545 $params2[$key] = $val;
547 if ($immediate) {
548 $params2['openid.mode'] = 'checkid_setup';
549 $ret['openid.mode'] = ($version >= 2.0) ? 'setup_needed': 'id_res';
550 $ret['openid.user_setup_url'] = $this->_loginUrl
551 . (strpos($this->_loginUrl, '?') === false ? '?' : '&')
552 . Zend_OpenId::paramsToQuery($params2);
553 return $ret;
554 } else {
555 /* Redirect to Server Login Screen */
556 Zend_OpenId::redirect($this->_loginUrl, $params2, $response);
557 return true;
561 if (!Zend_OpenId_Extension::forAll($extensions, 'parseRequest', $params)) {
562 $ret['openid.mode'] = ($immediate && $version >= 2.0) ? 'setup_needed': 'cancel';
563 return $ret;
566 /* Check if user trusts to the consumer */
567 $trusted = null;
568 $sites = $this->_storage->getTrustedSites($params['openid_identity']);
569 if (isset($params['openid_return_to'])) {
570 $root = $params['openid_return_to'];
572 if (isset($sites[$root])) {
573 $trusted = $sites[$root];
574 } else {
575 foreach ($sites as $site => $t) {
576 if (strpos($root, $site) === 0) {
577 $trusted = $t;
578 break;
579 } else {
580 /* OpenID 2.0 (9.2) check for realm wild-card matching */
581 $n = strpos($site, '://*.');
582 if ($n != false) {
583 $regex = '/^'
584 . preg_quote(substr($site, 0, $n+3), '/')
585 . '[A-Za-z1-9_\.]+?'
586 . preg_quote(substr($site, $n+4), '/')
587 . '/';
588 if (preg_match($regex, $root)) {
589 $trusted = $t;
590 break;
597 if (is_array($trusted)) {
598 if (!Zend_OpenId_Extension::forAll($extensions, 'checkTrustData', $trusted)) {
599 $trusted = null;
603 if ($trusted === false) {
604 $ret['openid.mode'] = 'cancel';
605 return $ret;
606 } else if ($trusted === null) {
607 /* Redirect to Server Trust Screen */
608 $params2 = array();
609 foreach ($params as $key => $val) {
610 if (strpos($key, 'openid_ns_') === 0) {
611 $key = 'openid.ns.' . substr($key, strlen('openid_ns_'));
612 } else if (strpos($key, 'openid_sreg_') === 0) {
613 $key = 'openid.sreg.' . substr($key, strlen('openid_sreg_'));
614 } else if (strpos($key, 'openid_') === 0) {
615 $key = 'openid.' . substr($key, strlen('openid_'));
617 $params2[$key] = $val;
619 if ($immediate) {
620 $params2['openid.mode'] = 'checkid_setup';
621 $ret['openid.mode'] = ($version >= 2.0) ? 'setup_needed': 'id_res';
622 $ret['openid.user_setup_url'] = $this->_trustUrl
623 . (strpos($this->_trustUrl, '?') === false ? '?' : '&')
624 . Zend_OpenId::paramsToQuery($params2);
625 return $ret;
626 } else {
627 Zend_OpenId::redirect($this->_trustUrl, $params2, $response);
628 return true;
632 return $this->_respond($version, $ret, $params, $extensions);
636 * Perepares information to send back to consumer's authentication request,
637 * signs it using shared secret and send back through HTTP redirection
639 * @param array $params GET or POST request variables
640 * @param mixed $extensions extension object or array of extensions objects
641 * @param Zend_Controller_Response_Abstract $response an optional response
642 * object to perform HTTP or HTML form redirection
643 * @return bool
645 public function respondToConsumer($params, $extensions=null,
646 Zend_Controller_Response_Abstract $response = null)
648 $version = 1.1;
649 if (isset($params['openid_ns']) &&
650 $params['openid_ns'] == Zend_OpenId::NS_2_0) {
651 $version = 2.0;
653 $ret = array();
654 if ($version >= 2.0) {
655 $ret['openid.ns'] = Zend_OpenId::NS_2_0;
657 $ret = $this->_respond($version, $ret, $params, $extensions);
658 if (!empty($params['openid_return_to'])) {
659 Zend_OpenId::redirect($params['openid_return_to'], $ret, $response);
661 return true;
665 * Perepares information to send back to consumer's authentication request
666 * and signs it using shared secret.
668 * @param float $version OpenID protcol version
669 * @param array $ret arguments to be send back to consumer
670 * @param array $params GET or POST request variables
671 * @param mixed $extensions extension object or array of extensions objects
672 * @return array
674 protected function _respond($version, $ret, $params, $extensions=null)
676 if (empty($params['openid_assoc_handle']) ||
677 !$this->_storage->getAssociation($params['openid_assoc_handle'],
678 $macFunc, $secret, $expires)) {
679 /* Use dumb mode */
680 if (!empty($params['openid_assoc_handle'])) {
681 $ret['openid.invalidate_handle'] = $params['openid_assoc_handle'];
683 $macFunc = $version >= 2.0 ? 'sha256' : 'sha1';
684 $secret = $this->_genSecret($macFunc);
685 $handle = uniqid();
686 $expiresIn = $this->_sessionTtl;
687 $this->_storage->addAssociation($handle,
688 $macFunc, $secret, time() + $expiresIn);
689 $ret['openid.assoc_handle'] = $handle;
690 } else {
691 $ret['openid.assoc_handle'] = $params['openid_assoc_handle'];
693 if (isset($params['openid_return_to'])) {
694 $ret['openid.return_to'] = $params['openid_return_to'];
696 if (isset($params['openid_claimed_id'])) {
697 $ret['openid.claimed_id'] = $params['openid_claimed_id'];
699 if (isset($params['openid_identity'])) {
700 $ret['openid.identity'] = $params['openid_identity'];
703 if ($version >= 2.0) {
704 if (!empty($this->_opEndpoint)) {
705 $ret['openid.op_endpoint'] = $this->_opEndpoint;
706 } else {
707 $ret['openid.op_endpoint'] = Zend_OpenId::selfUrl();
710 $ret['openid.response_nonce'] = gmdate('Y-m-d\TH:i:s\Z') . uniqid();
711 $ret['openid.mode'] = 'id_res';
713 Zend_OpenId_Extension::forAll($extensions, 'prepareResponse', $ret);
715 $signed = '';
716 $data = '';
717 foreach ($ret as $key => $val) {
718 if (strpos($key, 'openid.') === 0) {
719 $key = substr($key, strlen('openid.'));
720 if (!empty($signed)) {
721 $signed .= ',';
723 $signed .= $key;
724 $data .= $key . ':' . $val . "\n";
727 $signed .= ',signed';
728 $data .= 'signed:' . $signed . "\n";
729 $ret['openid.signed'] = $signed;
731 $ret['openid.sig'] = base64_encode(
732 Zend_OpenId::hashHmac($macFunc, $data, $secret));
734 return $ret;
738 * Performs authentication validation for dumb consumers
739 * Returns array of variables to push back to consumer.
740 * It MUST contain 'is_valid' variable with value 'true' or 'false'.
742 * @param float $version OpenID version
743 * @param array $params GET or POST request variables
744 * @return array
746 protected function _checkAuthentication($version, $params)
748 $ret = array();
749 if ($version >= 2.0) {
750 $ret['ns'] = Zend_OpenId::NS_2_0;
752 $ret['openid.mode'] = 'id_res';
754 if (empty($params['openid_assoc_handle']) ||
755 empty($params['openid_signed']) ||
756 empty($params['openid_sig']) ||
757 !$this->_storage->getAssociation($params['openid_assoc_handle'],
758 $macFunc, $secret, $expires)) {
759 $ret['is_valid'] = 'false';
760 return $ret;
763 $signed = explode(',', $params['openid_signed']);
764 $data = '';
765 foreach ($signed as $key) {
766 $data .= $key . ':';
767 if ($key == 'mode') {
768 $data .= "id_res\n";
769 } else {
770 $data .= $params['openid_' . strtr($key,'.','_')]."\n";
773 if (base64_decode($params['openid_sig']) ===
774 Zend_OpenId::hashHmac($macFunc, $data, $secret)) {
775 $ret['is_valid'] = 'true';
776 } else {
777 $ret['is_valid'] = 'false';
779 return $ret;