~clinton-collins/familyproject/trunk

« back to all changes in this revision

Viewing changes to ZendFramework/library/Zend/OpenId/Provider.php

  • Committer: Clinton Collins
  • Date: 2009-06-26 19:54:58 UTC
  • Revision ID: clinton.collins@gmail.com-20090626195458-5ebba0qcvo15xlpy
Initial Import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
 
 
3
/**
 
4
 * Zend Framework
 
5
 *
 
6
 * LICENSE
 
7
 *
 
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.
 
15
 *
 
16
 * @category   Zend
 
17
 * @package    Zend_OpenId
 
18
 * @subpackage Zend_OpenId_Provider
 
19
 * @copyright  Copyright (c) 2005-2008 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 13522 2009-01-06 16:35:55Z thomas $
 
22
 */
 
23
 
 
24
/**
 
25
 * @see Zend_OpenId
 
26
 */
 
27
require_once "Zend/OpenId.php";
 
28
 
 
29
/**
 
30
 * @see Zend_OpenId_Extension
 
31
 */
 
32
require_once "Zend/OpenId/Extension.php";
 
33
 
 
34
/**
 
35
 * OpenID provider (server) implementation
 
36
 *
 
37
 * @category   Zend
 
38
 * @package    Zend_OpenId
 
39
 * @subpackage Zend_OpenId_Provider
 
40
 * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
 
41
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 
42
 */
 
43
class Zend_OpenId_Provider
 
44
{
 
45
 
 
46
    /**
 
47
     * Reference to an implementation of storage object
 
48
     *
 
49
     * @var Zend_OpenId_Provider_Storage $_storage
 
50
     */
 
51
    private $_storage;
 
52
 
 
53
    /**
 
54
     * Reference to an implementation of user object
 
55
     *
 
56
     * @var Zend_OpenId_Provider_User $_user
 
57
     */
 
58
    private $_user;
 
59
 
 
60
    /**
 
61
     * Time to live of association session in secconds
 
62
     *
 
63
     * @var integer $_sessionTtl
 
64
     */
 
65
    private $_sessionTtl;
 
66
 
 
67
    /**
 
68
     * URL to peform interactive user login
 
69
     *
 
70
     * @var string $_loginUrl
 
71
     */
 
72
    private $_loginUrl;
 
73
 
 
74
    /**
 
75
     * URL to peform interactive validation of consumer by user
 
76
     *
 
77
     * @var string $_trustUrl
 
78
     */
 
79
    private $_trustUrl;
 
80
 
 
81
    /**
 
82
     * The OP Endpoint URL
 
83
     *
 
84
     * @var string $_opEndpoint
 
85
     */
 
86
    private $_opEndpoint;
 
87
 
 
88
    /**
 
89
     * Constructs a Zend_OpenId_Provider object with given parameters.
 
90
     *
 
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.
 
106
     */
 
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)
 
112
    {
 
113
        if ($loginUrl === null) {
 
114
            $loginUrl = Zend_OpenId::selfUrl() . '?openid.action=login';
 
115
        } else {
 
116
            $loginUrl = Zend_OpenId::absoluteUrl($loginUrl);
 
117
        }
 
118
        $this->_loginUrl = $loginUrl;
 
119
        if ($trustUrl === null) {
 
120
            $trustUrl = Zend_OpenId::selfUrl() . '?openid.action=trust';
 
121
        } else {
 
122
            $trustUrl = Zend_OpenId::absoluteUrl($trustUrl);
 
123
        }
 
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;
 
130
        }
 
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;
 
136
        }
 
137
        $this->_sessionTtl = $sessionTtl;
 
138
    }
 
139
 
 
140
    /**
 
141
     * Sets the OP Endpoint URL
 
142
     *
 
143
     * @param string $url the OP Endpoint URL
 
144
     * @return null
 
145
     */
 
146
    public function setOpEndpoint($url)
 
147
    {
 
148
        $this->_opEndpoint = $url;
 
149
    }
 
150
 
 
151
    /**
 
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
 
155
     *
 
156
     * @param string $id user identity URL
 
157
     * @param string $password encoded user password
 
158
     * @return bool
 
159
     */
 
160
    public function register($id, $password)
 
161
    {
 
162
        if (!Zend_OpenId::normalize($id) || empty($id)) {
 
163
            return false;
 
164
        }
 
165
        return $this->_storage->addUser($id, md5($id.$password));
 
166
    }
 
167
 
 
168
    /**
 
169
     * Returns true if user with given $id exists and false otherwise
 
170
     *
 
171
     * @param string $id user identity URL
 
172
     * @return bool
 
173
     */
 
174
    public function hasUser($id) {
 
175
        if (!Zend_OpenId::normalize($id)) {
 
176
            return false;
 
177
        }
 
178
        return $this->_storage->hasUser($id);
 
179
    }
 
180
 
 
181
    /**
 
182
     * Performs login of user with given $id and $password
 
183
     * Returns true in case of success and false otherwise
 
184
     *
 
185
     * @param string $id user identity URL
 
186
     * @param string $password user password
 
187
     * @return bool
 
188
     */
 
189
    public function login($id, $password)
 
190
    {
 
191
        if (!Zend_OpenId::normalize($id)) {
 
192
            return false;
 
193
        }
 
194
        if (!$this->_storage->checkUser($id, md5($id.$password))) {
 
195
            return false;
 
196
        }
 
197
        $this->_user->setLoggedInUser($id);
 
198
        return true;
 
199
    }
 
200
 
 
201
    /**
 
202
     * Performs logout. Clears information about logged in user.
 
203
     *
 
204
     * @return void
 
205
     */
 
206
    public function logout()
 
207
    {
 
208
        $this->_user->delLoggedInUser();
 
209
        return true;
 
210
    }
 
211
 
 
212
    /**
 
213
     * Returns identity URL of current logged in user or false
 
214
     *
 
215
     * @return mixed
 
216
     */
 
217
    public function getLoggedInUser() {
 
218
        return $this->_user->getLoggedInUser();
 
219
    }
 
220
 
 
221
    /**
 
222
     * Retrieve consumer's root URL from request query.
 
223
     * Returns URL or false in case of failure
 
224
     *
 
225
     * @param array $params query arguments
 
226
     * @return mixed
 
227
     */
 
228
    public function getSiteRoot($params)
 
229
    {
 
230
        $version = 1.1;
 
231
        if (isset($params['openid_ns']) &&
 
232
            $params['openid_ns'] == Zend_OpenId::NS_2_0) {
 
233
            $version = 2.0;
 
234
        }
 
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;
 
243
        }
 
244
        if (Zend_OpenId::normalizeUrl($root) && !empty($root)) {
 
245
            return $root;
 
246
        }
 
247
        return false;
 
248
    }
 
249
 
 
250
    /**
 
251
     * Allows consumer with given root URL to authenticate current logged
 
252
     * in user. Returns true on success and false on error.
 
253
     *
 
254
     * @param string $root root URL
 
255
     * @param mixed $extensions extension object or array of extensions objects
 
256
     * @return bool
 
257
     */
 
258
    public function allowSite($root, $extensions=null)
 
259
    {
 
260
        $id = $this->getLoggedInUser();
 
261
        if ($id === false) {
 
262
            return false;
 
263
        }
 
264
        if ($extensions !== null) {
 
265
            $data = array();
 
266
            Zend_OpenId_Extension::forAll($extensions, 'getTrustData', $data);
 
267
        } else {
 
268
            $data = true;
 
269
        }
 
270
        $this->_storage->addSite($id, $root, $data);
 
271
        return true;
 
272
    }
 
273
 
 
274
    /**
 
275
     * Prohibit consumer with given root URL to authenticate current logged
 
276
     * in user. Returns true on success and false on error.
 
277
     *
 
278
     * @param string $root root URL
 
279
     * @return bool
 
280
     */
 
281
    public function denySite($root)
 
282
    {
 
283
        $id = $this->getLoggedInUser();
 
284
        if ($id === false) {
 
285
            return false;
 
286
        }
 
287
        $this->_storage->addSite($id, $root, false);
 
288
        return true;
 
289
    }
 
290
 
 
291
    /**
 
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.
 
296
     *
 
297
     * @param string $root root URL
 
298
     * @return bool
 
299
     */
 
300
    public function delSite($root)
 
301
    {
 
302
        $id = $this->getLoggedInUser();
 
303
        if ($id === false) {
 
304
            return false;
 
305
        }
 
306
        $this->_storage->addSite($id, $root, null);
 
307
        return true;
 
308
    }
 
309
 
 
310
    /**
 
311
     * Returns list of known consumers for current logged in user or false
 
312
     * if he is not logged in.
 
313
     *
 
314
     * @return mixed
 
315
     */
 
316
    public function getTrustedSites()
 
317
    {
 
318
        $id = $this->getLoggedInUser();
 
319
        if ($id === false) {
 
320
            return false;
 
321
        }
 
322
        return $this->_storage->getTrustedSites($id);
 
323
    }
 
324
 
 
325
    /**
 
326
     * Handles HTTP request from consumer
 
327
     *
 
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
 
335
     */
 
336
    public function handle($params=null, $extensions=null,
 
337
                           Zend_Controller_Response_Abstract $response = null)
 
338
    {
 
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;
 
346
            }
 
347
        }
 
348
        $version = 1.1;
 
349
        if (isset($params['openid_ns']) &&
 
350
            $params['openid_ns'] == Zend_OpenId::NS_2_0) {
 
351
            $version = 2.0;
 
352
        }
 
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";
 
359
                }
 
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);
 
366
                }
 
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);
 
373
                }
 
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";
 
380
                }
 
381
                return $ret;
 
382
            }
 
383
        }
 
384
        return false;
 
385
    }
 
386
 
 
387
    /**
 
388
     * Generates a secret key for given hash function, returns RAW key or false
 
389
     * if function is not supported
 
390
     *
 
391
     * @param string $func hash function (sha1 or sha256)
 
392
     * @return mixed
 
393
     */
 
394
    protected function _genSecret($func)
 
395
    {
 
396
        if ($func == 'sha1') {
 
397
            $macLen = 20; /* 160 bit */
 
398
        } else if ($func == 'sha256') {
 
399
            $macLen = 32; /* 256 bit */
 
400
        } else {
 
401
            return false;
 
402
        }
 
403
        return Zend_OpenId::randomBytes($macLen);
 
404
    }
 
405
 
 
406
    /**
 
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.
 
410
     *
 
411
     * @param float $version OpenID version
 
412
     * @param array $params GET or POST request variables
 
413
     * @return array
 
414
     */
 
415
    protected function _associate($version, $params)
 
416
    {
 
417
        $ret = array();
 
418
 
 
419
        if ($version >= 2.0) {
 
420
            $ret['ns'] = Zend_OpenId::NS_2_0;
 
421
        }
 
422
 
 
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;
 
434
        }
 
435
 
 
436
        $ret['assoc_type'] = $params['openid_assoc_type'];
 
437
 
 
438
        $secret = $this->_genSecret($macFunc);
 
439
 
 
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;
 
454
        }
 
455
 
 
456
        if (isset($params['openid_session_type'])) {
 
457
            $ret['session_type'] = $params['openid_session_type'];
 
458
        }
 
459
 
 
460
        if (isset($dhFunc)) {
 
461
            if (empty($params['openid_dh_consumer_public'])) {
 
462
                $ret['error'] = 'Wrong "openid.dh_consumer_public"';
 
463
                return $ret;
 
464
            }
 
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']);
 
469
            }
 
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']);
 
474
            }
 
475
 
 
476
            $dh = Zend_OpenId::createDhKey($p, $g);
 
477
            $dh_details = Zend_OpenId::getDhKeyDetails($dh);
 
478
 
 
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;
 
485
            }
 
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);
 
490
        }
 
491
 
 
492
        $handle = uniqid();
 
493
        $expiresIn = $this->_sessionTtl;
 
494
 
 
495
        $ret['assoc_handle'] = $handle;
 
496
        $ret['expires_in'] = $expiresIn;
 
497
 
 
498
        $this->_storage->addAssociation($handle,
 
499
            $macFunc, $secret, time() + $expiresIn);
 
500
 
 
501
        return $ret;
 
502
    }
 
503
 
 
504
    /**
 
505
     * Performs authentication (or authentication check).
 
506
     *
 
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
 
513
     */
 
514
    protected function _checkId($version, $params, $immediate, $extensions=null,
 
515
        Zend_Controller_Response_Abstract $response = null)
 
516
    {
 
517
        $ret = array();
 
518
 
 
519
        if ($version >= 2.0) {
 
520
            $ret['openid.ns'] = Zend_OpenId::NS_2_0;
 
521
        }
 
522
        $root = $this->getSiteRoot($params);
 
523
        if ($root === false) {
 
524
            return false;
 
525
        }
 
526
 
 
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;
 
531
        }
 
532
 
 
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_'));
 
544
                }
 
545
                $params2[$key] = $val;
 
546
            }
 
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;
 
558
            }
 
559
        }
 
560
 
 
561
        if (!Zend_OpenId_Extension::forAll($extensions, 'parseRequest', $params)) {
 
562
            $ret['openid.mode'] = ($immediate && $version >= 2.0) ? 'setup_needed': 'cancel';
 
563
            return $ret;
 
564
        }
 
565
 
 
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'];
 
571
        }
 
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;
 
591
                        }
 
592
                    }
 
593
                }
 
594
            }
 
595
        }
 
596
 
 
597
        if (is_array($trusted)) {
 
598
            if (!Zend_OpenId_Extension::forAll($extensions, 'checkTrustData', $trusted)) {
 
599
                $trusted = null;
 
600
            }
 
601
        }
 
602
 
 
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_'));
 
616
                }
 
617
                $params2[$key] = $val;
 
618
            }
 
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;
 
629
            }
 
630
        }
 
631
 
 
632
        return $this->_respond($version, $ret, $params, $extensions);
 
633
    }
 
634
 
 
635
    /**
 
636
     * Perepares information to send back to consumer's authentication request,
 
637
     * signs it using shared secret and send back through HTTP redirection
 
638
     *
 
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
 
644
     */
 
645
    public function respondToConsumer($params, $extensions=null,
 
646
                           Zend_Controller_Response_Abstract $response = null)
 
647
    {
 
648
        $version = 1.1;
 
649
        if (isset($params['openid_ns']) &&
 
650
            $params['openid_ns'] == Zend_OpenId::NS_2_0) {
 
651
            $version = 2.0;
 
652
        }
 
653
        $ret = array();
 
654
        if ($version >= 2.0) {
 
655
            $ret['openid.ns'] = Zend_OpenId::NS_2_0;
 
656
        }
 
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);
 
660
        }
 
661
        return true;
 
662
    }
 
663
 
 
664
    /**
 
665
     * Perepares information to send back to consumer's authentication request
 
666
     * and signs it using shared secret.
 
667
     *
 
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
 
673
     */
 
674
    protected function _respond($version, $ret, $params, $extensions=null)
 
675
    {
 
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'];
 
682
            }
 
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'];
 
692
        }
 
693
        if (isset($params['openid_return_to'])) {
 
694
            $ret['openid.return_to'] = $params['openid_return_to'];
 
695
        }
 
696
        if (isset($params['openid_claimed_id'])) {
 
697
            $ret['openid.claimed_id'] = $params['openid_claimed_id'];
 
698
        }
 
699
        if (isset($params['openid_identity'])) {
 
700
            $ret['openid.identity'] = $params['openid_identity'];
 
701
        }
 
702
 
 
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();
 
708
            }
 
709
        }
 
710
        $ret['openid.response_nonce'] = gmdate('Y-m-d\TH:i:s\Z') . uniqid();
 
711
        $ret['openid.mode'] = 'id_res';
 
712
 
 
713
        Zend_OpenId_Extension::forAll($extensions, 'prepareResponse', $ret);
 
714
 
 
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 .= ',';
 
722
                }
 
723
                $signed .= $key;
 
724
                $data .= $key . ':' . $val . "\n";
 
725
            }
 
726
        }
 
727
        $signed .= ',signed';
 
728
        $data .= 'signed:' . $signed . "\n";
 
729
        $ret['openid.signed'] = $signed;
 
730
 
 
731
        $ret['openid.sig'] = base64_encode(
 
732
            Zend_OpenId::hashHmac($macFunc, $data, $secret));
 
733
 
 
734
        return $ret;
 
735
    }
 
736
 
 
737
    /**
 
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'.
 
741
     *
 
742
     * @param float $version OpenID version
 
743
     * @param array $params GET or POST request variables
 
744
     * @return array
 
745
     */
 
746
    protected function _checkAuthentication($version, $params)
 
747
    {
 
748
        $ret = array();
 
749
        if ($version >= 2.0) {
 
750
            $ret['ns'] = Zend_OpenId::NS_2_0;
 
751
        }
 
752
        $ret['openid.mode'] = 'id_res';
 
753
 
 
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;
 
761
        }
 
762
 
 
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";
 
771
            }
 
772
        }
 
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';
 
778
        }
 
779
        return $ret;
 
780
    }
 
781
}