*/
public function validate($token, $seed = '', $timeout = null, $unique = false)
{
- $b = Horde_Url::uriB64Decode($token);
- $nonce = substr($b, 0, 6);
- $hash = substr($b, 6);
+ list($nonce, $hash) = $this->_decode($token);
if ($hash != $this->_hash($nonce . $seed)) {
return false;
}
- $timestamp = unpack('N', substr($nonce, 0, 4));
- $timestamp = array_pop($timestamp);
if ($timeout === null) {
$timeout = $this->_params['token_lifetime'];
}
- if ($timeout >= 0 && (time() - $timestamp - $timeout) >= 0) {
+ if ($this->_isExpired($nonce, $timeout)) {
return false;
}
if ($unique) {
return true;
}
+ /**
+ * Is the given token still valid? Throws an exception in case it is not.
+ *
+ * @param string $token The signed token.
+ * @param string $seed The unique ID of the token.
+nce?
+ *
+ * @return array An array of two elements: The nonce and the hash.
+ *
+ * @throws Horde_Token_Exception If the token was invalid.
+ */
+ public function isValid($token, $seed = '')
+ {
+ list($nonce, $hash) = $this->_decode($token);
+ if ($hash != $this->_hash($nonce . $seed)) {
+ throw new Horde_Token_Exception_Invalid('We cannot verify that this request was really sent by you. It could be a malicious request. If you intended to perform this action, you can retry it now.');
+ }
+ if ($this->_isExpired($nonce, $this->_params['token_lifetime'])) {
+ throw new Horde_Token_Exception_Expired(sprintf("This request cannot be completed because the link you followed or the form you submitted was only valid for %s minutes. Please try again now.", floor($this->_params['token_lifetime'] / 60)));
+ }
+ return array($nonce, $hash);
+ }
+
+ /**
+ * Is the given token valid and has never been used before? Throws an
+ * exception otherwise.
+ *
+ * @param string $token The signed token.
+ * @param string $seed The unique ID of the token.
+nce?
+ *
+ * @return NULL
+ *
+ * @throws Horde_Token_Exception If the token was invalid or has been used before.
+ */
+ public function isValidAndUnused($token, $seed = '')
+ {
+ list($nonce, $hash) = $this->isValid($token, $seed);
+ if (!$this->verify($nonce)) {
+ throw new Horde_Token_Exception_Used('This token has been used before!');
+ }
+ }
+
+ /**
+ * Decode a token into the prefixed nonce and the hash.
+ *
+ * @param string $token The token to be decomposed.
+ *
+ * @return array An array of two elements: The nonce and the hash.
+ */
+ private function _decode($token)
+ {
+ $b = Horde_Url::uriB64Decode($token);
+ return array(substr($b, 0, 6), substr($b, 6));
+ }
+
+ /**
+ * Has the nonce expired?
+ *
+ * @param string $nonce The to be checked for expiration.
+ * @param int $timeout The timeout that should be applied.
+ *
+ * @return boolean True if the nonce expired.
+ */
+ private function _isExpired($nonce, $timeout)
+ {
+ $timestamp = unpack('N', substr($nonce, 0, 4));
+ $timestamp = array_pop($timestamp);
+ return $timeout >= 0 && (time() - $timestamp - $timeout) >= 0;
+ }
+
+ /**
+ * Sign the given text with the secret.
+ *
+ * @param string $text The text to be signed.
+ *
+ * @return string The hashed text.
+ */
private function _hash($text)
{
return hash('sha256', $text . $this->_params['secret'], true);
* @category Horde
* @package Token
*/
-class Horde_Token_Exception extends Horde_Exception_Prior
+class Horde_Token_Exception extends Horde_Exception
{
}
--- /dev/null
+<?php
+/**
+ * Indicates an expired token.
+ *
+ * PHP version 5
+ *
+ * @category Horde
+ * @package Token
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Token
+ */
+
+/**
+ * Indicates an expired token.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Horde
+ * @package Token
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Token
+ */
+class Horde_Token_Exception_Expired extends Horde_Token_Exception
+{
+}
--- /dev/null
+<?php
+/**
+ * Indicates an invalid token.
+ *
+ * PHP version 5
+ *
+ * @category Horde
+ * @package Token
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Token
+ */
+
+/**
+ * Indicates an invalid token.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Horde
+ * @package Token
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Token
+ */
+class Horde_Token_Exception_Invalid extends Horde_Token_Exception
+{
+}
--- /dev/null
+<?php
+/**
+ * Indicates a used token.
+ *
+ * PHP version 5
+ *
+ * @category Horde
+ * @package Token
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Token
+ */
+
+/**
+ * Indicates a used token.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Horde
+ * @package Token
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @license http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link http://pear.horde.org/index.php?package=Token
+ */
+class Horde_Token_Exception_Used extends Horde_Token_Exception
+{
+}
<?xml version="1.0" encoding="UTF-8"?>
-<package packagerversion="1.4.9" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
-http://pear.php.net/dtd/tasks-1.0.xsd
-http://pear.php.net/dtd/package-2.0
-http://pear.php.net/dtd/package-2.0.xsd">
+<package packagerversion="1.9.1" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
<name>Token</name>
<channel>pear.horde.org</channel>
<summary>Horde Token API</summary>
<description>The Horde_Token:: class provides a common abstracted interface
into the various token generation mediums. It also includes all of the
- functions for retrieving, storing, and checking tokens.
- </description>
+ functions for retrieving, storing, and checking tokens.</description>
<lead>
<name>Chuck Hagenbuch</name>
<user>chuck</user>
<email>chuck@horde.org</email>
<active>yes</active>
</lead>
- <date>2009-02-21</date>
+ <date>2010-11-30</date>
+ <time>13:33:08</time>
<version>
<release>0.1.0</release>
<api>0.1.0</api>
<api>beta</api>
</stability>
<license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
- <notes>* Add Horde_Token_Exception::.
+ <notes>
+* Add Horde_Token_Exception::.
* Move driver code into sub-classes.
* Use exceptions, not PEAR_Errors.
* Initial Horde 4 package.
</notes>
<contents>
- <dir name="/">
+ <dir baseinstalldir="/" name="/">
<dir name="lib">
<dir name="Horde">
<dir name="Token">
+ <dir name="Exception">
+ <file name="Expired.php" role="php" />
+ <file name="Invalid.php" role="php" />
+ <file name="Used.php" role="php" />
+ </dir> <!-- /lib/Horde/Token/Exception -->
<file name="Base.php" role="php" />
<file name="Exception.php" role="php" />
<file name="File.php" role="php" />
<file name="Token.php" role="php" />
</dir> <!-- /lib/Horde -->
</dir> <!-- /lib -->
+ <dir name="test">
+ <dir name="Horde">
+ <dir name="Token">
+ <dir name="Unit">
+ <file name="FileTest.php" role="test" />
+ </dir> <!-- /test/Horde/Token/Unit -->
+ <file name="AllTests.php" role="test" />
+ <file name="Autoload.php" role="test" />
+ <file name="phpunit.xml" role="test" />
+ </dir> <!-- /test/Horde/Token -->
+ </dir> <!-- /test/Horde -->
+ </dir> <!-- /test -->
</dir> <!-- / -->
</contents>
<dependencies>
</dependencies>
<phprelease>
<filelist>
- <install name="lib/Horde/Token/Base.php" as="Horde/Token/Base.php" />
- <install name="lib/Horde/Token/Exception.php" as="Horde/Token/Exception.php" />
- <install name="lib/Horde/Token/File.php" as="Horde/Token/File.php" />
- <install name="lib/Horde/Token/Null.php" as="Horde/Token/Null.php" />
- <install name="lib/Horde/Token/Sql.php" as="Horde/Token/Sql.php" />
- <install name="lib/Horde/Token.php" as="Horde/Token.php" />
+ <install as="Horde/Token.php" name="lib/Horde/Token.php" />
+ <install as="Horde/Token/Base.php" name="lib/Horde/Token/Base.php" />
+ <install as="Horde/Token/Exception.php" name="lib/Horde/Token/Exception.php" />
+ <install as="Horde/Token/File.php" name="lib/Horde/Token/File.php" />
+ <install as="Horde/Token/Null.php" name="lib/Horde/Token/Null.php" />
+ <install as="Horde/Token/Sql.php" name="lib/Horde/Token/Sql.php" />
+ <install as="Horde/Token/Exception/Expired.php" name="lib/Horde/Token/Exception/Expired.php" />
+ <install as="Horde/Token/Exception/Invalid.php" name="lib/Horde/Token/Exception/Invalid.php" />
+ <install as="Horde/Token/Exception/Used.php" name="lib/Horde/Token/Exception/Used.php" />
+ <install as="Horde/Token/AllTests.php" name="test/Horde/Token/AllTests.php" />
+ <install as="Horde/Token/Autoload.php" name="test/Horde/Token/Autoload.php" />
+ <install as="Horde/Token/phpunit.xml" name="test/Horde/Token/phpunit.xml" />
+ <install as="Horde/Token/Unit/FileTest.php" name="test/Horde/Token/Unit/FileTest.php" />
</filelist>
</phprelease>
<changelog>
<release>
<version>
- <release>0.0.4</release>
- <api>0.0.4</api>
+ <release>0.0.1</release>
+ <api>0.0.1</api>
+ </version>
+ <stability>
+ <release>alpha</release>
+ <api>alpha</api>
+ </stability>
+ <date>2003-07-03</date>
+ <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+ <notes>
+Initial release as a PEAR package
+ </notes>
+ </release>
+ <release>
+ <version>
+ <release>0.0.2</release>
+ <api>0.0.2</api>
</version>
<stability>
<release>beta</release>
<api>beta</api>
</stability>
- <date>2006-05-08</date>
- <time>23:48:27</time>
+ <date>2004-11-23</date>
<license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
- <notes>Converted to package.xml 2.0 for pear.horde.org
+ <notes>
+Remove assumption of IPv4 IP address format
</notes>
</release>
<release>
</stability>
<date>2005-01-01</date>
<license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
- <notes>* Add support for separate read and write DB servers for the sql driver.
+ <notes>
+* Add support for separate read and write DB servers for the sql driver.
</notes>
</release>
<release>
<version>
- <release>0.0.2</release>
- <api>0.0.2</api>
+ <release>0.0.4</release>
+ <api>0.0.4</api>
</version>
<stability>
<release>beta</release>
<api>beta</api>
</stability>
- <date>2004-11-23</date>
+ <date>2006-05-08</date>
+ <time>23:48:27</time>
<license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
- <notes>Remove assumption of IPv4 IP address format
+ <notes>
+Converted to package.xml 2.0 for pear.horde.org
</notes>
</release>
<release>
<version>
- <release>0.0.1</release>
- <api>0.0.1</api>
+ <release>0.1.0</release>
+ <api>0.1.0</api>
</version>
<stability>
- <release>alpha</release>
- <api>alpha</api>
+ <release>beta</release>
+ <api>beta</api>
</stability>
- <date>2003-07-03</date>
+ <date>2010-11-30</date>
<license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
- <notes>Initial release as a PEAR package
+ <notes>
+* Add Horde_Token_Exception::.
+ * Move driver code into sub-classes.
+ * Use exceptions, not PEAR_Errors.
+ * Initial Horde 4 package.
</notes>
</release>
</changelog>
require_once dirname(__FILE__) . '/../Autoload.php';
/**
- * Base for session testing.
+ * Test the file based token backend.
*
* Copyright 2010 The Horde Project (http://www.horde.org/)
*
}
/**
+ * @expectedException Horde_Token_Exception_Invalid
+ */
+ public function testInvalidTokenException()
+ {
+ $t = new Horde_Token_File(array('secret' => 'abc'));
+ $t->isValid('something');
+ }
+
+ /**
+ * @expectedException Horde_Token_Exception_Invalid
+ */
+ public function testInvalidSeedException()
+ {
+ $t = new Horde_Token_File(array('secret' => 'abc'));
+ $t->isValid($t->get('a'), 'b');
+ }
+
+ /**
+ * @expectedException Horde_Token_Exception_Expired
+ */
+ public function testTimeoutException()
+ {
+ $t = new Horde_Token_File(
+ array(
+ 'secret' => 'abc',
+ 'token_lifetime' => 1
+ )
+ );
+ $token = $t->get('a');
+ sleep(1);
+ $t->isValid($token, 'a');
+ }
+
+ /**
+ * @expectedException Horde_Token_Exception_Used
+ */
+ public function testIsValidAndUnusedException()
+ {
+ $t = new Horde_Token_File(
+ array(
+ 'secret' => 'abc',
+ 'token_dir' => $this->_getTemporaryDirectory()
+ )
+ );
+ $token = $t->get('a');
+ $t->isValidAndUnused($token, 'a');
+ $t->isValidAndUnused($token, 'a');
+ }
+
+ /**
* @expectedException Horde_Token_Exception
*/
public function testInvalidConstruction()