Index: typo3/sysext/openid/ext_tables.sql =================================================================== --- typo3/sysext/openid/ext_tables.sql (revision 6513) +++ typo3/sysext/openid/ext_tables.sql (working copy) @@ -2,12 +2,46 @@ # Table structure for table 'be_users' # CREATE TABLE be_users ( - tx_openid_openid varchar(255) DEFAULT '' NOT NULL + tx_openid_openid varchar(255) DEFAULT '' NOT NULL, ); # # Table structure for table 'fe_users' # CREATE TABLE fe_users ( - tx_openid_openid varchar(255) DEFAULT '' NOT NULL -); \ No newline at end of file + tx_openid_openid varchar(255) DEFAULT '' NOT NULL, +); + +# +# Table structure for table 'tx_openid_assoc_store'. +# +CREATE TABLE tx_openid_assoc_store ( + uid int(11) unsigned NOT NULL auto_increment, + pid int(11) unsigned DEFAULT '0' NOT NULL, + crdate int(11) unsigned DEFAULT '0' NOT NULL, + tstamp int(11) unsigned DEFAULT '0' NOT NULL, + expires int(11) unsigned DEFAULT '0' NOT NULL, + server_url varchar(2047) DEFAULT '' NOT NULL, + assoc_handle varchar(255) DEFAULT '' NOT NULL, + content blob, + + PRIMARY KEY (uid), + KEY assoc_handle (assoc_handle(8)), + KEY expires (expires) +) ENGINE=InnoDB; + +# +# Table structure for table 'tx_openid_nonce_store'. +# +CREATE TABLE tx_openid_nonce_store ( + uid int(11) unsigned NOT NULL auto_increment, + pid int(11) unsigned DEFAULT '0' NOT NULL, + crdate int(11) unsigned DEFAULT '0' NOT NULL, + tstamp int(11) unsigned DEFAULT '0' NOT NULL, + server_url varchar(2047) DEFAULT '' NOT NULL, + salt char(40) DEFAULT '' NOT NULL, + + PRIMARY KEY (uid), + UNIQUE KEY nonce (server_url(255),tstamp,salt), + KEY crdate (crdate) +) ENGINE=InnoDB; Index: typo3/sysext/openid/TODO =================================================================== --- typo3/sysext/openid/TODO (revision 6513) +++ typo3/sysext/openid/TODO (working copy) @@ -1,2 +1 @@ - -* use DB (the sessions or the caching framework) instead of the filesystem to store OpenID data (class.tx_openid_sv1.php) +None \ No newline at end of file Index: typo3/sysext/openid/sv1/class.tx_openid_store.php =================================================================== --- typo3/sysext/openid/sv1/class.tx_openid_store.php (revision 0) +++ typo3/sysext/openid/sv1/class.tx_openid_store.php (revision 0) @@ -0,0 +1,251 @@ + + * @package TYPO3 + * @subpackage tx_openid + */ +class tx_openid_store extends Auth_OpenID_OpenIDStore { + + const ASSOCIATION_TABLE_NAME = 'tx_openid_assoc_store'; + + const ASSOCIATION_EXPIRATION_SAFETY_INTERVAL = 120; /* 2 minutes */ + + const NONCE_TABLE_NAME = 'tx_openid_nonce_store'; + + const NONCE_STORAGE_TIME = 864000; /* 10 days */ + + /** + * Sores the association for future use + * + * @param string $serverUrl Server URL + * @param Auth_OpenID_Association $association OpenID association + * @return void + */ + public function storeAssociation($serverUrl, $association) { + /* @var $association Auth_OpenID_Association */ + $GLOBALS['TYPO3_DB']->sql_query('START TRANSACTION'); + + if ($this->doesAssociationExist($serverUrl, $association->handle)) { + $this->updateExistingAssociation($serverUrl, $association); + } + else { + $this->storeNewAssociation($serverUrl, $association); + } + + $GLOBALS['TYPO3_DB']->sql_query('COMMIT'); + } + + /** + * Removes all expired associations. + * + * @return int A number of removed associations + */ + public function cleanupAssociations() { + $where = sprintf('expires<=%d', time()); + $GLOBALS['TYPO3_DB']->exec_DELETEquery(self::ASSOCIATION_TABLE_NAME, $where); + return $GLOBALS['TYPO3_DB']->sql_affected_rows(); + } + + /** + * Obtains the association to the server + * + * @param string $serverUrl Server URL + * @param string $handle Association handle (optional) + * @return Auth_OpenID_Association + */ + public function getAssociation($serverUrl, $handle = null) { + $this->cleanupAssociations(); + + $where = sprintf('server_url=%s AND expires>%d', + $GLOBALS['TYPO3_DB']->fullQuoteStr($serverUrl, self::ASSOCIATION_TABLE_NAME), + time()); + if ($handle != null) { + $where .= sprintf(' AND assoc_handle=%s', + $GLOBALS['TYPO3_DB']->fullQuoteStr($handle, self::ASSOCIATION_TABLE_NAME)); + $sort = ''; + } + else { + $sort = 'tstamp DESC'; + } + list($row) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,content', + self::ASSOCIATION_TABLE_NAME, $where, '', $sort, '1'); + + $result = null; + if (is_array($row)) { + $result = @unserialize($row['content']); + $this->updateAssociationTimeStamp($row['tstamp']); + } + return $result; + } + + /** + * Removes the association + * + * @param string $serverUrl Server URL + * @param string $handle Association handle (optional) + * @return boolean true if the association existed + */ + function removeAssociation($serverUrl, $handle) { + $where = sprintf('server_url=%s AND assoc_handle=%s', + $GLOBALS['TYPO3_DB']->fullQuoteStr($serverUrl, self::ASSOCIATION_TABLE_NAME), + $GLOBALS['TYPO3_DB']->fullQuoteStr($handle, self::ASSOCIATION_TABLE_NAME)); + $GLOBALS['TYPO3_DB']->exec_DELETEquery(self::ASSOCIATION_TABLE_NAME, $where); + $deletedCount = $GLOBALS['TYPO3_DB']->sql_affected_rows(); + return ($deletedCount > 0); + } + + /** + * Removes old nonces + * + * @return void + */ + public function cleanupNonces() { + $where = sprintf('crdate<%d', time() - self::NONCE_STORAGE_TIME); + $GLOBALS['TYPO3_DB']->exec_DELETEquery(self::NONCE_TABLE_NAME, $where); + } + + /** + * Checks if this nonce was already used + * @param $serverUrl Server URL + * @param $timestamp Time stamp + * @param $salt Nonce value + * @return boolean true if nonce was not used before anc can be used now + */ + public function useNonce($serverUrl, $timestamp, $salt) { + $result = false; + + if (abs($timestamp - time()) < $GLOBALS['Auth_OpenID_SKEW']) { + $values = array( + 'crdate' => time(), + 'salt' => $salt, + 'server_url' => $serverUrl, + 'tstamp' => $timestamp + ); + $GLOBALS['TYPO3_DB']->exec_INSERTquery(self::NONCE_TABLE_NAME, + $values); + $affectedRows = $GLOBALS['TYPO3_DB']->sql_affected_rows(); + $result = ($affectedRows > 0); + } + + return $result; + } + + /** + * Resets the store by removing all data in it + * + * @return void + */ + public function reset() { + $GLOBALS['TYPO3_DB']->exec_DELETEquery(self::ASSOCIATION_TABLE_NAME, '1=1'); + $GLOBALS['TYPO3_DB']->exec_DELETEquery(self::NONCE_TABLE_NAME, '1=1'); + } + + /** + * Checks if such association exists. + * + * @param string $serverUrl Server URL + * @param Auth_OpenID_Association $association OpenID association + * @return boolean + */ + protected function doesAssociationExist($serverUrl, $association) { + $where = sprintf('server_url=%s AND assoc_handle=%s AND expires>%d', + $GLOBALS['TYPO3_DB']->fullQuoteStr($serverUrl, self::ASSOCIATION_TABLE_NAME), + $GLOBALS['TYPO3_DB']->fullQuoteStr($association->handle, self::ASSOCIATION_TABLE_NAME), + time()); + list($row) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( + 'COUNT(*) as assocCount', self::ASSOCIATION_TABLE_NAME, $where); + return ($row['assocCount'] > 0); + } + + /** + * Updates existing association. + * + * @param string $serverUrl Server URL + * @param Auth_OpenID_Association $association OpenID association + * @return void + */ + protected function updateExistingAssociation($serverUrl, Auth_OpenID_Association $association) { + $where = sprintf('server_url=%s AND assoc_handle=%s AND expires>%d', + $GLOBALS['TYPO3_DB']->fullQuoteStr($serverUrl, self::ASSOCIATION_TABLE_NAME), + $GLOBALS['TYPO3_DB']->fullQuoteStr($association->handle, self::ASSOCIATION_TABLE_NAME), + time()); + $serializedAssociation = serialize($association); + $values = array( + 'content' => $serializedAssociation, + 'tstamp' => time(), + ); + $GLOBALS['TYPO3_DB']->exec_UPDATEquery(self::ASSOCIATION_TABLE_NAME, $where, $values); + } + + /** + * Stores new association to the database. + * + * @param $serverUrl Server URL + * @param $association OpenID association + * @return void + */ + protected function storeNewAssociation($serverUrl, $association) { + $serializedAssociation = serialize($association); + $values = array( + 'assoc_handle' => $association->handle, + 'content' => $serializedAssociation, + 'crdate' => $association->issued, + 'tstamp' => time(), + 'expires' => $association->issued + $association->lifetime - self::ASSOCIATION_EXPIRATION_SAFETY_INTERVAL, + 'server_url' => $serverUrl + ); + // In the next query we can get race conditions. sha1_hash prevents many + // asociations from being stored for one server + $GLOBALS['TYPO3_DB']->exec_INSERTquery(self::ASSOCIATION_TABLE_NAME, $values); + } + + /** + * Updates association time stamp. + * + * @param $recordId Association record id in the database + * @return void + */ + protected function updateAssociationTimeStamp($recordId) { + $where = sprintf('uid=%d', $recordId); + $values = array( + 'tstamp' => time() + ); + $GLOBALS['TYPO3_DB']->exec_UPDATEquery(self::ASSOCIATION_TABLE_NAME, $where, $values); + } +} + +if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/openid/class.tx_openid_store.php']) { + include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/openid/class.tx_openid_store.php']); +} + +?> \ No newline at end of file Index: typo3/sysext/openid/sv1/class.tx_openid_sv1.php =================================================================== --- typo3/sysext/openid/sv1/class.tx_openid_sv1.php (revision 6513) +++ typo3/sysext/openid/sv1/class.tx_openid_sv1.php (working copy) @@ -44,6 +44,7 @@ */ require_once(PATH_t3lib . 'class.t3lib_svbase.php'); +require_once(t3lib_extMgm::extPath('openid', 'sv1/class.tx_openid_store.php')); /** * Service "OpenID Authentication" for the "openid" extension. @@ -276,7 +277,6 @@ // Include files require_once($phpOpenIDLibPath . '/Auth/OpenID/Consumer.php'); - require_once($phpOpenIDLibPath . '/Auth/OpenID/FileStore.php'); // Restore path @set_include_path($oldIncludePath); @@ -319,16 +319,10 @@ * @return Auth_OpenID_Consumer Consumer instance */ protected function getOpenIDConsumer() { - // TODO Change this to a TYPO3-specific database-based store in future. - // File-based store is ineffective and insecure. After changing - // get rid of the FileStore include in includePHPOpenIDLibrary() - $openIDStorePath = PATH_site . 'typo3temp' . DIRECTORY_SEPARATOR . 'tx_openid'; + $openIDStore = t3lib_div::makeInstance('tx_openid_store'); + /* @var $openIDStore tx_openid_store */ + $openIDStore->cleanup(); - // For now we just prevent any web access to these files - if (!file_exists($openIDStorePath . '/.htaccess')) { - file_put_contents($openIDStorePath . '/.htaccess', 'deny from all'); - } - $openIDStore = new Auth_OpenID_FileStore($openIDStorePath); return new Auth_OpenID_Consumer($openIDStore); } Index: typo3/sysext/openid/ext_emconf.php =================================================================== --- typo3/sysext/openid/ext_emconf.php (revision 6513) +++ typo3/sysext/openid/ext_emconf.php (working copy) @@ -24,12 +24,12 @@ 'state' => 'beta', 'internal' => '', 'uploadfolder' => 0, - 'createDirs' => 'typo3temp/tx_openid', + 'createDirs' => '', 'modify_tables' => 'fe_users,be_users', 'clearCacheOnLoad' => 0, 'lockType' => 'system', 'author_company' => 'TYPO3 core team', - 'version' => '0.1.0', + 'version' => '1.0.0', 'constraints' => array( 'depends' => array( 'typo3' => '4.3.0-0.0.0', @@ -47,4 +47,4 @@ '_md5_values_when_last_written' => 'a:56:{s:4:"TODO";s:4:"977e";s:23:"class.tx_openid_eid.php";s:4:"e8aa";s:29:"class.tx_openid_mod_setup.php";s:4:"1c9d";s:26:"class.tx_openid_return.php";s:4:"b508";s:12:"ext_icon.gif";s:4:"f1e1";s:17:"ext_localconf.php";s:4:"20c4";s:14:"ext_tables.php";s:4:"20c5";s:14:"ext_tables.sql";s:4:"f309";s:17:"locallang_csh.xml";s:4:"7e8a";s:21:"locallang_csh_mod.xml";s:4:"fe98";s:16:"locallang_db.xml";s:4:"0952";s:14:"doc/manual.sxw";s:4:"05d1";s:22:"lib/php-openid/COPYING";s:4:"3b83";s:25:"lib/php-openid/README.txt";s:4:"eb02";s:37:"lib/php-openid/php-openid-typo3.patch";s:4:"b2fb";s:30:"lib/php-openid/Auth/OpenID.php";s:4:"3be9";s:33:"lib/php-openid/Auth/OpenID/AX.php";s:4:"b68e";s:42:"lib/php-openid/Auth/OpenID/Association.php";s:4:"9b1e";s:38:"lib/php-openid/Auth/OpenID/BigMath.php";s:4:"a56d";s:39:"lib/php-openid/Auth/OpenID/Consumer.php";s:4:"94cd";s:40:"lib/php-openid/Auth/OpenID/CryptUtil.php";s:4:"6276";s:49:"lib/php-openid/Auth/OpenID/DatabaseConnection.php";s:4:"660d";s:44:"lib/php-openid/Auth/OpenID/DiffieHellman.php";s:4:"1a0b";s:39:"lib/php-openid/Auth/OpenID/Discover.php";s:4:"1a9b";s:40:"lib/php-openid/Auth/OpenID/DumbStore.php";s:4:"c1e9";s:40:"lib/php-openid/Auth/OpenID/Extension.php";s:4:"5aae";s:40:"lib/php-openid/Auth/OpenID/FileStore.php";s:4:"69da";s:35:"lib/php-openid/Auth/OpenID/HMAC.php";s:4:"a0a3";s:40:"lib/php-openid/Auth/OpenID/Interface.php";s:4:"421b";s:37:"lib/php-openid/Auth/OpenID/KVForm.php";s:4:"3c7c";s:45:"lib/php-openid/Auth/OpenID/MemcachedStore.php";s:4:"cb6d";s:38:"lib/php-openid/Auth/OpenID/Message.php";s:4:"413e";s:41:"lib/php-openid/Auth/OpenID/MySQLStore.php";s:4:"4607";s:36:"lib/php-openid/Auth/OpenID/Nonce.php";s:4:"2738";s:35:"lib/php-openid/Auth/OpenID/PAPE.php";s:4:"decb";s:36:"lib/php-openid/Auth/OpenID/Parse.php";s:4:"28c9";s:46:"lib/php-openid/Auth/OpenID/PostgreSQLStore.php";s:4:"a2da";s:39:"lib/php-openid/Auth/OpenID/SQLStore.php";s:4:"29d2";s:42:"lib/php-openid/Auth/OpenID/SQLiteStore.php";s:4:"4855";s:35:"lib/php-openid/Auth/OpenID/SReg.php";s:4:"ae70";s:37:"lib/php-openid/Auth/OpenID/Server.php";s:4:"8121";s:44:"lib/php-openid/Auth/OpenID/ServerRequest.php";s:4:"d29d";s:40:"lib/php-openid/Auth/OpenID/TrustRoot.php";s:4:"2866";s:38:"lib/php-openid/Auth/OpenID/URINorm.php";s:4:"e4fb";s:41:"lib/php-openid/Auth/Yadis/HTTPFetcher.php";s:4:"bdaa";s:37:"lib/php-openid/Auth/Yadis/Manager.php";s:4:"ee7d";s:34:"lib/php-openid/Auth/Yadis/Misc.php";s:4:"65f6";s:49:"lib/php-openid/Auth/Yadis/ParanoidHTTPFetcher.php";s:4:"170e";s:39:"lib/php-openid/Auth/Yadis/ParseHTML.php";s:4:"d8f8";s:46:"lib/php-openid/Auth/Yadis/PlainHTTPFetcher.php";s:4:"6d0f";s:33:"lib/php-openid/Auth/Yadis/XML.php";s:4:"09f1";s:34:"lib/php-openid/Auth/Yadis/XRDS.php";s:4:"4bcd";s:33:"lib/php-openid/Auth/Yadis/XRI.php";s:4:"5eca";s:36:"lib/php-openid/Auth/Yadis/XRIRes.php";s:4:"9b44";s:35:"lib/php-openid/Auth/Yadis/Yadis.php";s:4:"d6ee";s:27:"sv1/class.tx_openid_sv1.php";s:4:"f697";}', ); -?> \ No newline at end of file +?>