|
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_Consumer |
|
19 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
20 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
21 * @version $Id: File.php 23161 2010-10-19 16:08:36Z matthew $ |
|
22 */ |
|
23 |
|
24 /** |
|
25 * @see Zend_OpenId_Consumer_Storage |
|
26 */ |
|
27 require_once "Zend/OpenId/Consumer/Storage.php"; |
|
28 |
|
29 /** |
|
30 * External storage implemmentation using serialized files |
|
31 * |
|
32 * @category Zend |
|
33 * @package Zend_OpenId |
|
34 * @subpackage Zend_OpenId_Consumer |
|
35 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
36 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
37 */ |
|
38 class Zend_OpenId_Consumer_Storage_File extends Zend_OpenId_Consumer_Storage |
|
39 { |
|
40 |
|
41 /** |
|
42 * Directory name to store data files in |
|
43 * |
|
44 * @var string $_dir |
|
45 */ |
|
46 private $_dir; |
|
47 |
|
48 /** |
|
49 * Constructs storage object and creates storage directory |
|
50 * |
|
51 * @param string $dir directory name to store data files in |
|
52 * @throws Zend_OpenId_Exception |
|
53 */ |
|
54 public function __construct($dir = null) |
|
55 { |
|
56 if ($dir === null) { |
|
57 $tmp = getenv('TMP'); |
|
58 if (empty($tmp)) { |
|
59 $tmp = getenv('TEMP'); |
|
60 if (empty($tmp)) { |
|
61 $tmp = "/tmp"; |
|
62 } |
|
63 } |
|
64 $user = get_current_user(); |
|
65 if (is_string($user) && !empty($user)) { |
|
66 $tmp .= '/' . $user; |
|
67 } |
|
68 $dir = $tmp . '/openid/consumer'; |
|
69 } |
|
70 $this->_dir = $dir; |
|
71 if (!is_dir($this->_dir)) { |
|
72 if (!@mkdir($this->_dir, 0700, 1)) { |
|
73 /** |
|
74 * @see Zend_OpenId_Exception |
|
75 */ |
|
76 require_once 'Zend/OpenId/Exception.php'; |
|
77 throw new Zend_OpenId_Exception( |
|
78 'Cannot access storage directory ' . $dir, |
|
79 Zend_OpenId_Exception::ERROR_STORAGE); |
|
80 } |
|
81 } |
|
82 if (($f = fopen($this->_dir.'/assoc.lock', 'w+')) === null) { |
|
83 /** |
|
84 * @see Zend_OpenId_Exception |
|
85 */ |
|
86 require_once 'Zend/OpenId/Exception.php'; |
|
87 throw new Zend_OpenId_Exception( |
|
88 'Cannot create a lock file in the directory ' . $dir, |
|
89 Zend_OpenId_Exception::ERROR_STORAGE); |
|
90 } |
|
91 fclose($f); |
|
92 if (($f = fopen($this->_dir.'/discovery.lock', 'w+')) === null) { |
|
93 /** |
|
94 * @see Zend_OpenId_Exception |
|
95 */ |
|
96 require_once 'Zend/OpenId/Exception.php'; |
|
97 throw new Zend_OpenId_Exception( |
|
98 'Cannot create a lock file in the directory ' . $dir, |
|
99 Zend_OpenId_Exception::ERROR_STORAGE); |
|
100 } |
|
101 fclose($f); |
|
102 if (($f = fopen($this->_dir.'/nonce.lock', 'w+')) === null) { |
|
103 /** |
|
104 * @see Zend_OpenId_Exception |
|
105 */ |
|
106 require_once 'Zend/OpenId/Exception.php'; |
|
107 throw new Zend_OpenId_Exception( |
|
108 'Cannot create a lock file in the directory ' . $dir, |
|
109 Zend_OpenId_Exception::ERROR_STORAGE); |
|
110 } |
|
111 fclose($f); |
|
112 } |
|
113 |
|
114 /** |
|
115 * Stores information about association identified by $url/$handle |
|
116 * |
|
117 * @param string $url OpenID server URL |
|
118 * @param string $handle assiciation handle |
|
119 * @param string $macFunc HMAC function (sha1 or sha256) |
|
120 * @param string $secret shared secret |
|
121 * @param long $expires expiration UNIX time |
|
122 * @return bool |
|
123 */ |
|
124 public function addAssociation($url, $handle, $macFunc, $secret, $expires) |
|
125 { |
|
126 $name1 = $this->_dir . '/assoc_url_' . md5($url); |
|
127 $name2 = $this->_dir . '/assoc_handle_' . md5($handle); |
|
128 $lock = @fopen($this->_dir . '/assoc.lock', 'w+'); |
|
129 if ($lock === false) { |
|
130 return false; |
|
131 } |
|
132 if (!flock($lock, LOCK_EX)) { |
|
133 fclose($lock); |
|
134 return false; |
|
135 } |
|
136 try { |
|
137 $f = @fopen($name1, 'w+'); |
|
138 if ($f === false) { |
|
139 fclose($lock); |
|
140 return false; |
|
141 } |
|
142 $data = serialize(array($url, $handle, $macFunc, $secret, $expires)); |
|
143 fwrite($f, $data); |
|
144 if (function_exists('symlink')) { |
|
145 @unlink($name2); |
|
146 if (symlink($name1, $name2)) { |
|
147 fclose($f); |
|
148 fclose($lock); |
|
149 return true; |
|
150 } |
|
151 } |
|
152 $f2 = @fopen($name2, 'w+'); |
|
153 if ($f2) { |
|
154 fwrite($f2, $data); |
|
155 fclose($f2); |
|
156 @unlink($name1); |
|
157 $ret = true; |
|
158 } else { |
|
159 $ret = false; |
|
160 } |
|
161 fclose($f); |
|
162 fclose($lock); |
|
163 return $ret; |
|
164 } catch (Exception $e) { |
|
165 fclose($lock); |
|
166 throw $e; |
|
167 } |
|
168 } |
|
169 |
|
170 /** |
|
171 * Gets information about association identified by $url |
|
172 * Returns true if given association found and not expired and false |
|
173 * otherwise |
|
174 * |
|
175 * @param string $url OpenID server URL |
|
176 * @param string &$handle assiciation handle |
|
177 * @param string &$macFunc HMAC function (sha1 or sha256) |
|
178 * @param string &$secret shared secret |
|
179 * @param long &$expires expiration UNIX time |
|
180 * @return bool |
|
181 */ |
|
182 public function getAssociation($url, &$handle, &$macFunc, &$secret, &$expires) |
|
183 { |
|
184 $name1 = $this->_dir . '/assoc_url_' . md5($url); |
|
185 $lock = @fopen($this->_dir . '/assoc.lock', 'w+'); |
|
186 if ($lock === false) { |
|
187 return false; |
|
188 } |
|
189 if (!flock($lock, LOCK_EX)) { |
|
190 fclose($lock); |
|
191 return false; |
|
192 } |
|
193 try { |
|
194 $f = @fopen($name1, 'r'); |
|
195 if ($f === false) { |
|
196 fclose($lock); |
|
197 return false; |
|
198 } |
|
199 $ret = false; |
|
200 $data = stream_get_contents($f); |
|
201 if (!empty($data)) { |
|
202 list($storedUrl, $handle, $macFunc, $secret, $expires) = unserialize($data); |
|
203 if ($url === $storedUrl && $expires > time()) { |
|
204 $ret = true; |
|
205 } else { |
|
206 $name2 = $this->_dir . '/assoc_handle_' . md5($handle); |
|
207 fclose($f); |
|
208 @unlink($name2); |
|
209 @unlink($name1); |
|
210 fclose($lock); |
|
211 return false; |
|
212 } |
|
213 } |
|
214 fclose($f); |
|
215 fclose($lock); |
|
216 return $ret; |
|
217 } catch (Exception $e) { |
|
218 fclose($lock); |
|
219 throw $e; |
|
220 } |
|
221 } |
|
222 |
|
223 /** |
|
224 * Gets information about association identified by $handle |
|
225 * Returns true if given association found and not expired and false |
|
226 * otherwise |
|
227 * |
|
228 * @param string $handle assiciation handle |
|
229 * @param string &$url OpenID server URL |
|
230 * @param string &$macFunc HMAC function (sha1 or sha256) |
|
231 * @param string &$secret shared secret |
|
232 * @param long &$expires expiration UNIX time |
|
233 * @return bool |
|
234 */ |
|
235 public function getAssociationByHandle($handle, &$url, &$macFunc, &$secret, &$expires) |
|
236 { |
|
237 $name2 = $this->_dir . '/assoc_handle_' . md5($handle); |
|
238 $lock = @fopen($this->_dir . '/assoc.lock', 'w+'); |
|
239 if ($lock === false) { |
|
240 return false; |
|
241 } |
|
242 if (!flock($lock, LOCK_EX)) { |
|
243 fclose($lock); |
|
244 return false; |
|
245 } |
|
246 try { |
|
247 $f = @fopen($name2, 'r'); |
|
248 if ($f === false) { |
|
249 fclose($lock); |
|
250 return false; |
|
251 } |
|
252 $ret = false; |
|
253 $data = stream_get_contents($f); |
|
254 if (!empty($data)) { |
|
255 list($url, $storedHandle, $macFunc, $secret, $expires) = unserialize($data); |
|
256 if ($handle === $storedHandle && $expires > time()) { |
|
257 $ret = true; |
|
258 } else { |
|
259 fclose($f); |
|
260 @unlink($name2); |
|
261 $name1 = $this->_dir . '/assoc_url_' . md5($url); |
|
262 @unlink($name1); |
|
263 fclose($lock); |
|
264 return false; |
|
265 } |
|
266 } |
|
267 fclose($f); |
|
268 fclose($lock); |
|
269 return $ret; |
|
270 } catch (Exception $e) { |
|
271 fclose($lock); |
|
272 throw $e; |
|
273 } |
|
274 } |
|
275 |
|
276 /** |
|
277 * Deletes association identified by $url |
|
278 * |
|
279 * @param string $url OpenID server URL |
|
280 * @return bool |
|
281 */ |
|
282 public function delAssociation($url) |
|
283 { |
|
284 $name1 = $this->_dir . '/assoc_url_' . md5($url); |
|
285 $lock = @fopen($this->_dir . '/assoc.lock', 'w+'); |
|
286 if ($lock === false) { |
|
287 return false; |
|
288 } |
|
289 if (!flock($lock, LOCK_EX)) { |
|
290 fclose($lock); |
|
291 return false; |
|
292 } |
|
293 try { |
|
294 $f = @fopen($name1, 'r'); |
|
295 if ($f === false) { |
|
296 fclose($lock); |
|
297 return false; |
|
298 } |
|
299 $data = stream_get_contents($f); |
|
300 if (!empty($data)) { |
|
301 list($storedUrl, $handle, $macFunc, $secret, $expires) = unserialize($data); |
|
302 if ($url === $storedUrl) { |
|
303 $name2 = $this->_dir . '/assoc_handle_' . md5($handle); |
|
304 fclose($f); |
|
305 @unlink($name2); |
|
306 @unlink($name1); |
|
307 fclose($lock); |
|
308 return true; |
|
309 } |
|
310 } |
|
311 fclose($f); |
|
312 fclose($lock); |
|
313 return true; |
|
314 } catch (Exception $e) { |
|
315 fclose($lock); |
|
316 throw $e; |
|
317 } |
|
318 } |
|
319 |
|
320 /** |
|
321 * Stores information discovered from identity $id |
|
322 * |
|
323 * @param string $id identity |
|
324 * @param string $realId discovered real identity URL |
|
325 * @param string $server discovered OpenID server URL |
|
326 * @param float $version discovered OpenID protocol version |
|
327 * @param long $expires expiration UNIX time |
|
328 * @return bool |
|
329 */ |
|
330 public function addDiscoveryInfo($id, $realId, $server, $version, $expires) |
|
331 { |
|
332 $name = $this->_dir . '/discovery_' . md5($id); |
|
333 $lock = @fopen($this->_dir . '/discovery.lock', 'w+'); |
|
334 if ($lock === false) { |
|
335 return false; |
|
336 } |
|
337 if (!flock($lock, LOCK_EX)) { |
|
338 fclose($lock); |
|
339 return false; |
|
340 } |
|
341 try { |
|
342 $f = @fopen($name, 'w+'); |
|
343 if ($f === false) { |
|
344 fclose($lock); |
|
345 return false; |
|
346 } |
|
347 $data = serialize(array($id, $realId, $server, $version, $expires)); |
|
348 fwrite($f, $data); |
|
349 fclose($f); |
|
350 fclose($lock); |
|
351 return true; |
|
352 } catch (Exception $e) { |
|
353 fclose($lock); |
|
354 throw $e; |
|
355 } |
|
356 } |
|
357 |
|
358 /** |
|
359 * Gets information discovered from identity $id |
|
360 * Returns true if such information exists and false otherwise |
|
361 * |
|
362 * @param string $id identity |
|
363 * @param string &$realId discovered real identity URL |
|
364 * @param string &$server discovered OpenID server URL |
|
365 * @param float &$version discovered OpenID protocol version |
|
366 * @param long &$expires expiration UNIX time |
|
367 * @return bool |
|
368 */ |
|
369 public function getDiscoveryInfo($id, &$realId, &$server, &$version, &$expires) |
|
370 { |
|
371 $name = $this->_dir . '/discovery_' . md5($id); |
|
372 $lock = @fopen($this->_dir . '/discovery.lock', 'w+'); |
|
373 if ($lock === false) { |
|
374 return false; |
|
375 } |
|
376 if (!flock($lock, LOCK_EX)) { |
|
377 fclose($lock); |
|
378 return false; |
|
379 } |
|
380 try { |
|
381 $f = @fopen($name, 'r'); |
|
382 if ($f === false) { |
|
383 fclose($lock); |
|
384 return false; |
|
385 } |
|
386 $ret = false; |
|
387 $data = stream_get_contents($f); |
|
388 if (!empty($data)) { |
|
389 list($storedId, $realId, $server, $version, $expires) = unserialize($data); |
|
390 if ($id === $storedId && $expires > time()) { |
|
391 $ret = true; |
|
392 } else { |
|
393 fclose($f); |
|
394 @unlink($name); |
|
395 fclose($lock); |
|
396 return false; |
|
397 } |
|
398 } |
|
399 fclose($f); |
|
400 fclose($lock); |
|
401 return $ret; |
|
402 } catch (Exception $e) { |
|
403 fclose($lock); |
|
404 throw $e; |
|
405 } |
|
406 } |
|
407 |
|
408 /** |
|
409 * Removes cached information discovered from identity $id |
|
410 * |
|
411 * @param string $id identity |
|
412 * @return bool |
|
413 */ |
|
414 public function delDiscoveryInfo($id) |
|
415 { |
|
416 $name = $this->_dir . '/discovery_' . md5($id); |
|
417 $lock = @fopen($this->_dir . '/discovery.lock', 'w+'); |
|
418 if ($lock === false) { |
|
419 return false; |
|
420 } |
|
421 if (!flock($lock, LOCK_EX)) { |
|
422 fclose($lock); |
|
423 return false; |
|
424 } |
|
425 try { |
|
426 @unlink($name); |
|
427 fclose($lock); |
|
428 return true; |
|
429 } catch (Exception $e) { |
|
430 fclose($lock); |
|
431 throw $e; |
|
432 } |
|
433 } |
|
434 |
|
435 /** |
|
436 * The function checks the uniqueness of openid.response_nonce |
|
437 * |
|
438 * @param string $provider openid.openid_op_endpoint field from authentication response |
|
439 * @param string $nonce openid.response_nonce field from authentication response |
|
440 * @return bool |
|
441 */ |
|
442 public function isUniqueNonce($provider, $nonce) |
|
443 { |
|
444 $name = $this->_dir . '/nonce_' . md5($provider.';'.$nonce); |
|
445 $lock = @fopen($this->_dir . '/nonce.lock', 'w+'); |
|
446 if ($lock === false) { |
|
447 return false; |
|
448 } |
|
449 if (!flock($lock, LOCK_EX)) { |
|
450 fclose($lock); |
|
451 return false; |
|
452 } |
|
453 try { |
|
454 $f = @fopen($name, 'x'); |
|
455 if ($f === false) { |
|
456 fclose($lock); |
|
457 return false; |
|
458 } |
|
459 fwrite($f, $provider.';'.$nonce); |
|
460 fclose($f); |
|
461 fclose($lock); |
|
462 return true; |
|
463 } catch (Exception $e) { |
|
464 fclose($lock); |
|
465 throw $e; |
|
466 } |
|
467 } |
|
468 |
|
469 /** |
|
470 * Removes data from the uniqueness database that is older then given date |
|
471 * |
|
472 * @param mixed $date date of expired data |
|
473 */ |
|
474 public function purgeNonces($date=null) |
|
475 { |
|
476 $lock = @fopen($this->_dir . '/nonce.lock', 'w+'); |
|
477 if ($lock !== false) { |
|
478 flock($lock, LOCK_EX); |
|
479 } |
|
480 try { |
|
481 if (!is_int($date) && !is_string($date)) { |
|
482 $nonceFiles = glob($this->_dir . '/nonce_*'); |
|
483 foreach ((array) $nonceFiles as $name) { |
|
484 @unlink($name); |
|
485 } |
|
486 unset($nonceFiles); |
|
487 } else { |
|
488 if (is_string($date)) { |
|
489 $time = time($date); |
|
490 } else { |
|
491 $time = $date; |
|
492 } |
|
493 $nonceFiles = glob($this->_dir . '/nonce_*'); |
|
494 foreach ((array) $nonceFiles as $name) { |
|
495 if (filemtime($name) < $time) { |
|
496 @unlink($name); |
|
497 } |
|
498 } |
|
499 unset($nonceFiles); |
|
500 } |
|
501 if ($lock !== false) { |
|
502 fclose($lock); |
|
503 } |
|
504 } catch (Exception $e) { |
|
505 if ($lock !== false) { |
|
506 fclose($lock); |
|
507 } |
|
508 throw $e; |
|
509 } |
|
510 } |
|
511 } |