|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 2008-2011 Dmitry Dulepov <dmitry@typo3.org> 00006 * All rights reserved 00007 * 00008 * This script is part of the TYPO3 project. The TYPO3 project is 00009 * free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * The GNU General Public License can be found at 00015 * http://www.gnu.org/copyleft/gpl.html. 00016 * 00017 * This script is distributed in the hope that it will be useful, 00018 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 * GNU General Public License for more details. 00021 * 00022 * This copyright notice MUST APPEAR in all copies of the script! 00023 ***************************************************************/ 00024 /** 00025 * [CLASS/FUNCTION INDEX of SCRIPT] 00026 * 00027 * 00028 * 00029 * 57: class tx_openid_sv1 extends t3lib_svbase 00030 * 92: public function init() 00031 * 119: public function initAuth($subType, array $loginData, array $authenticationInformation, t3lib_userAuth &$parentObject) 00032 * 139: public function getUser() 00033 * 176: public function authUser(array $userRecord) 00034 * 221: protected function includePHPOpenIDLibrary() 00035 * 250: protected function getUserRecord($openIDIdentifier) 00036 * 273: protected function getOpenIDConsumer() 00037 * 300: protected function sendOpenIDRequest() 00038 * 368: protected function getReturnURL() 00039 * 414: protected function writeLog($message) 00040 * 00041 * TOTAL FUNCTIONS: 10 00042 * (This index is automatically created/updated by the extension "extdeveval") 00043 * 00044 */ 00045 00046 require_once(PATH_t3lib . 'class.t3lib_svbase.php'); 00047 require_once(t3lib_extMgm::extPath('openid', 'sv1/class.tx_openid_store.php')); 00048 00049 /** 00050 * Service "OpenID Authentication" for the "openid" extension. 00051 * 00052 * $Id: class.tx_openid_sv1.php 10120 2011-01-18 20:03:36Z ohader $ 00053 * 00054 * @author Dmitry Dulepov <dmitry@typo3.org> 00055 * @package TYPO3 00056 * @subpackage tx_openid 00057 */ 00058 class tx_openid_sv1 extends t3lib_svbase { 00059 /** Class name */ 00060 public $prefixId = 'tx_openid_sv1'; // Same as class name 00061 00062 /** Path to this script relative to the extension directory */ 00063 public $scriptRelPath = 'sv1/class.tx_openid_sv1.php'; 00064 00065 /** The extension key */ 00066 public $extKey = 'openid'; 00067 00068 /** Login data as passed to initAuth() */ 00069 protected $loginData = array(); 00070 00071 /** 00072 * Additional authentication information provided by t3lib_userAuth. We use 00073 * it to decide what database table contains user records. 00074 */ 00075 protected $authenticationInformation = array(); 00076 00077 /** 00078 * OpenID response object. It is initialized when OpenID provider returns 00079 * with success/failure response to us. 00080 * 00081 * @var Auth_OpenID_ConsumerResponse 00082 */ 00083 protected $openIDResponse = null; 00084 00085 /** 00086 * A reference to the calling object 00087 * 00088 * @var t3lib_userAuth 00089 */ 00090 protected $parentObject; 00091 00092 /** 00093 * If set to true, than libraries are already included. 00094 */ 00095 protected static $openIDLibrariesIncluded = false; 00096 00097 /** 00098 * Contructs the OpenID authentication service. 00099 */ 00100 public function __construct() { 00101 // Auth_Yadis_Yadis::getHTTPFetcher() will use a cURL fetcher if the functionality 00102 // is available in PHP, however the TYPO3 setting is not considered here: 00103 if (!defined('Auth_Yadis_CURL_OVERRIDE')) { 00104 if (!$GLOBALS['TYPO3_CONF_VARS']['SYS']['curlUse']) { 00105 define('Auth_Yadis_CURL_OVERRIDE', true); 00106 } 00107 } 00108 } 00109 00110 /** 00111 * Checks if service is available,. In case of this service we check that 00112 * prerequesties for "PHP OpenID" libraries are fulfilled: 00113 * - GMP or BCMATH PHP extensions are installed and functional 00114 * - set_include_path() PHP function is available 00115 * 00116 * @return boolean true if service is available 00117 */ 00118 public function init() { 00119 $available = false; 00120 if (extension_loaded('gmp')) { 00121 $available = is_callable('gmp_init'); 00122 } elseif (extension_loaded('bcmath')) { 00123 $available = is_callable('bcadd'); 00124 } else { 00125 $this->writeLog('Neither bcmath, nor gmp PHP extension found. OpenID authentication will not be available.'); 00126 } 00127 // We also need set_include_path() PHP function 00128 if (!is_callable('set_include_path')) { 00129 $available = false; 00130 $this->writeDevLog('set_include_path() PHP function is not available. OpenID authentication is disabled.'); 00131 } 00132 return $available ? parent::init() : false; 00133 } 00134 00135 /** 00136 * Initializes authentication for this service. 00137 * 00138 * @param string $subType: Subtype for authentication (either "getUserFE" or "getUserBE") 00139 * @param array $loginData: Login data submitted by user and preprocessed by t3lib/class.t3lib_userauth.php 00140 * @param array $authenticationInformation: Additional TYPO3 information for authentication services (unused here) 00141 * @param t3lib_userAuth $parentObject: Calling object 00142 * @return void 00143 */ 00144 public function initAuth($subType, array $loginData, array $authenticationInformation, t3lib_userAuth &$parentObject) { 00145 // Store login and authetication data 00146 $this->loginData = $loginData; 00147 $this->authenticationInformation = $authenticationInformation; 00148 // If we are here after authentication by the OpenID server, get its response. 00149 if (t3lib_div::_GP('tx_openid_mode') == 'finish' && $this->openIDResponse == null) { 00150 $this->includePHPOpenIDLibrary(); 00151 $openIDConsumer = $this->getOpenIDConsumer(); 00152 $this->openIDResponse = $openIDConsumer->complete($this->getReturnURL()); 00153 } 00154 $this->parentObject = $parentObject; 00155 } 00156 00157 /** 00158 * This function returns the user record back to the t3lib_userAuth. it does not 00159 * mean that user is authenticated, it means only that user is found. This 00160 * function makes sure that user cannot be authenticated by any other service 00161 * if user tries to use OpenID to authenticate. 00162 * 00163 * @return mixed User record (content of fe_users/be_users as appropriate for the current mode) 00164 */ 00165 public function getUser() { 00166 $userRecord = null; 00167 if ($this->loginData['status'] == 'login') { 00168 if ($this->openIDResponse instanceof Auth_OpenID_ConsumerResponse) { 00169 $GLOBALS['BACK_PATH'] = $this->getBackPath(); 00170 // We are running inside the OpenID return script 00171 // Note: we cannot use $this->openIDResponse->getDisplayIdentifier() 00172 // because it may return a different identifier. For example, 00173 // LiveJournal server converts all underscore characters in the 00174 // original identfier to dashes. 00175 if ($this->openIDResponse->status == Auth_OpenID_SUCCESS) { 00176 $openIDIdentifier = $this->getFinalOpenIDIdentifier(); 00177 if ($openIDIdentifier) { 00178 $userRecord = $this->getUserRecord($openIDIdentifier); 00179 if ($userRecord != null) { 00180 $this->writeLog('User \'%s\' logged in with OpenID \'%s\'', 00181 $userRecord[$this->parentObject->formfield_uname], $openIDIdentifier); 00182 } else { 00183 $this->writeLog('Failed to login user using OpenID \'%s\'', 00184 $openIDIdentifier); 00185 } 00186 } 00187 } 00188 } else { 00189 // Here if user just started authentication 00190 $userRecord = $this->getUserRecord($this->loginData['uname']); 00191 } 00192 // The above function will return user record from the OpenID. It means that 00193 // user actually tried to authenticate using his OpenID. In this case 00194 // we must change the password in the record to a long random string so 00195 // that this user cannot be authenticated with other service. 00196 if (is_array($userRecord)) { 00197 $userRecord[$this->authenticationInformation['db_user']['userident_column']] = uniqid($this->prefixId . LF, true); 00198 } 00199 } 00200 return $userRecord; 00201 } 00202 00203 /** 00204 * Authenticates user using OpenID. 00205 * 00206 * @param array $userRecord User record 00207 * @return int Code that shows if user is really authenticated. 00208 * @see t3lib_userAuth::checkAuthentication() 00209 */ 00210 public function authUser(array $userRecord) { 00211 $result = 0; // 0 means authentication failure 00212 00213 if ($userRecord['tx_openid_openid'] == '') { 00214 // If user does not have OpenID, let other services to try (code 100) 00215 $result = 100; 00216 } else { 00217 // Check if user is identified by the OpenID 00218 if ($this->openIDResponse instanceof Auth_OpenID_ConsumerResponse) { 00219 // If we have a response, it means OpenID server tried to authenticate 00220 // the user. Now we just look what is the status and provide 00221 // corresponding response to the caller 00222 if ($this->openIDResponse->status == Auth_OpenID_SUCCESS) { 00223 // Success (code 200) 00224 $result = 200; 00225 } else { 00226 $this->writeDevLog('OpenID authentication failed with code \'%s\'.', 00227 $this->openIDResponse->status); 00228 } 00229 } else { 00230 // We may need to send a request to the OpenID server. 00231 // Check if the user identifier looks like OpenID user identifier first. 00232 // Prevent PHP warning in case if identifiers is not an OpenID identifier 00233 // (not an URL). 00234 $urlParts = @parse_url($this->loginData['uname']); 00235 if (is_array($urlParts) && $urlParts['scheme'] != '' && $urlParts['host']) { 00236 // Yes, this looks like a good OpenID. Ask OpenID server (should not return) 00237 $this->sendOpenIDRequest(); 00238 // If we are here, it means we have a valid OpenID but failed to 00239 // contact the server. We stop authentication process. 00240 // Alternatively it may mean that OpenID format is not correct. 00241 // In both cases we return code 0 (complete failure) 00242 } else { 00243 $result = 100; 00244 } 00245 } 00246 } 00247 00248 return $result; 00249 } 00250 00251 /** 00252 * Includes necessary files for the PHP OpenID library 00253 * 00254 * @return void 00255 */ 00256 protected function includePHPOpenIDLibrary() { 00257 if (!self::$openIDLibrariesIncluded) { 00258 00259 // Prevent further calls 00260 self::$openIDLibrariesIncluded = true; 00261 00262 // PHP OpenID libraries requires adjustments of path settings 00263 $oldIncludePath = get_include_path(); 00264 $phpOpenIDLibPath = t3lib_extMgm::extPath('openid') . 'lib/php-openid'; 00265 @set_include_path($phpOpenIDLibPath . PATH_SEPARATOR . 00266 $phpOpenIDLibPath . PATH_SEPARATOR . 'Auth' . 00267 PATH_SEPARATOR . $oldIncludePath); 00268 00269 // Make sure that random generator is properly set up. Constant could be 00270 // defined by the previous inclusion of the file 00271 if (!defined('Auth_OpenID_RAND_SOURCE')) { 00272 if (TYPO3_OS == 'WIN') { 00273 // No random generator on Windows! 00274 define('Auth_OpenID_RAND_SOURCE', null); 00275 } elseif (!is_readable('/dev/urandom')) { 00276 if (is_readable('/dev/random')) { 00277 define('Auth_OpenID_RAND_SOURCE', '/dev/random'); 00278 } else { 00279 define('Auth_OpenID_RAND_SOURCE', null); 00280 } 00281 } 00282 } 00283 00284 // Include files 00285 require_once($phpOpenIDLibPath . '/Auth/OpenID/Consumer.php'); 00286 00287 // Restore path 00288 @set_include_path($oldIncludePath); 00289 00290 if (!is_array($_SESSION)) { 00291 // Yadis requires session but session is not initialized when 00292 // processing Backend authentication 00293 @session_start(); 00294 $this->writeLog('Session is initialized'); 00295 } 00296 } 00297 } 00298 00299 /** 00300 * Gets user record for the user with the OpenID provided by the user 00301 * 00302 * @param string $openIDIdentifier OpenID identifier to search for 00303 * @return array Database fields from the table that corresponds to the current login mode (FE/BE) 00304 */ 00305 protected function getUserRecord($openIDIdentifier) { 00306 $record = null; 00307 if ($openIDIdentifier) { 00308 $record = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', 00309 $this->authenticationInformation['db_user']['table'], 00310 'tx_openid_openid=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($openIDIdentifier, $this->authenticationInformation['db_user']['table']) . 00311 $this->authenticationInformation['db_user']['check_pid_clause'] . 00312 $this->authenticationInformation['db_user']['enable_clause']); 00313 } else { 00314 // This should never happen and generally means hack attempt. 00315 // We just log it and do not return any records. 00316 $this->writeLog('getUserRecord is called with the empty OpenID'); 00317 } 00318 return $record; 00319 } 00320 00321 /** 00322 * Creates OpenID Consumer object with a TYPO3-specific store. This function 00323 * is almost identical to the example from the PHP OpenID library. 00324 * @todo use DB (or the caching framework) instead of the filesystem to store OpenID data 00325 * @return Auth_OpenID_Consumer Consumer instance 00326 */ 00327 protected function getOpenIDConsumer() { 00328 $openIDStore = t3lib_div::makeInstance('tx_openid_store'); 00329 /* @var $openIDStore tx_openid_store */ 00330 $openIDStore->cleanup(); 00331 00332 return new Auth_OpenID_Consumer($openIDStore); 00333 } 00334 00335 /** 00336 * Sends request to the OpenID server to authenticate the user with the 00337 * given ID. This function is almost identical to the example from the PHP 00338 * OpenID library. Due to the OpenID specification we cannot do a slient login. 00339 * Sometimes we have to redirect to the OpenID provider web site so that 00340 * user can enter his password there. In this case we will redirect and provide 00341 * a return adress to the special script inside this directory, which will 00342 * handle the result appropriately. 00343 * 00344 * This function does not return on success. If it returns, it means something 00345 * went totally wrong with OpenID. 00346 * 00347 * @return void 00348 */ 00349 protected function sendOpenIDRequest() { 00350 $this->includePHPOpenIDLibrary(); 00351 00352 $openIDIdentifier = $this->loginData['uname']; 00353 00354 // Initialize OpenID client system, get the consumer 00355 $openIDConsumer = $this->getOpenIDConsumer(); 00356 00357 // Begin the OpenID authentication process 00358 $authenticationRequest = $openIDConsumer->begin($openIDIdentifier); 00359 if (!$authenticationRequest) { 00360 // Not a valid OpenID. Since it can be some other ID, we just return 00361 // and let other service handle it. 00362 $this->writeLog('Could not create authentication request for OpenID identifier \'%s\'', $openIDIdentifier); 00363 return; 00364 } 00365 00366 // Redirect the user to the OpenID server for authentication. 00367 // Store the token for this authentication so we can verify the 00368 // response. 00369 00370 // For OpenID version 1, we *should* send a redirect. For OpenID version 2, 00371 // we should use a Javascript form to send a POST request to the server. 00372 $returnURL = $this->getReturnURL(); 00373 $trustedRoot = t3lib_div::getIndpEnv('TYPO3_SITE_URL'); 00374 00375 if ($authenticationRequest->shouldSendRedirect()) { 00376 $redirectURL = $authenticationRequest->redirectURL($trustedRoot, $returnURL); 00377 00378 // If the redirect URL can't be built, return. We can only return. 00379 if (Auth_OpenID::isFailure($redirectURL)) { 00380 $this->writeLog('Authentication request could not create redirect URL for OpenID identifier \'%s\'', $openIDIdentifier); 00381 return; 00382 } 00383 00384 // Send redirect. We use 303 code because it allows to redirect POST 00385 // requests without resending the form. This is exactly what we need here. 00386 // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4 00387 @ob_end_clean(); 00388 t3lib_utility_Http::redirect($redirectURL, t3lib_utility_Http::HTTP_STATUS_303); 00389 } else { 00390 $formHtml = $authenticationRequest->htmlMarkup($trustedRoot, 00391 $returnURL, false, array('id' => 'openid_message')); 00392 00393 // Display an error if the form markup couldn't be generated; 00394 // otherwise, render the HTML. 00395 if (Auth_OpenID::isFailure($formHtml)) { 00396 // Form markup cannot be generated 00397 $this->writeLog('Could not create form markup for OpenID identifier \'%s\'', $openIDIdentifier); 00398 return; 00399 } else { 00400 @ob_end_clean(); 00401 echo $formHtml; 00402 } 00403 } 00404 // If we reached this point, we must not return! 00405 exit; 00406 } 00407 00408 /** 00409 * Creates return URL for the OpenID server. When a user is authenticated by 00410 * the OpenID server, the user will be sent to this URL to complete 00411 * authentication process with the current site. We send it to our script. 00412 * 00413 * @return string Return URL 00414 */ 00415 protected function getReturnURL() { 00416 if ($this->authenticationInformation['loginType'] == 'FE') { 00417 // We will use eID to send user back, create session data and 00418 // return to the calling page. 00419 // Notice: 'pid' and 'logintype' parameter names cannot be changed! 00420 // They are essential for FE user authentication. 00421 $returnURL = 'index.php?eID=tx_openid&' . 00422 'pid=' . $this->authenticationInformation['db_user']['checkPidList'] . '&' . 00423 'logintype=login&'; 00424 } else { 00425 // In the Backend we will use dedicated script to create session. 00426 // It is much easier for the Backend to manage users. 00427 // Notice: 'login_status' parameter name cannot be changed! 00428 // It is essential for BE user authentication. 00429 $absoluteSiteURL = substr(t3lib_div::getIndpEnv('TYPO3_SITE_URL'), strlen(t3lib_div::getIndpEnv('TYPO3_REQUEST_HOST'))); 00430 $returnURL = $absoluteSiteURL . TYPO3_mainDir . 'sysext/' . $this->extKey . '/class.tx_openid_return.php?login_status=login&'; 00431 } 00432 if (t3lib_div::_GP('tx_openid_mode') == 'finish') { 00433 $requestURL = t3lib_div::_GP('tx_openid_location'); 00434 $claimedIdentifier = t3lib_div::_GP('tx_openid_claimed'); 00435 } else { 00436 $requestURL = t3lib_div::getIndpEnv('TYPO3_REQUEST_URL'); 00437 $claimedIdentifier = $this->loginData['uname']; 00438 } 00439 $returnURL .= 'tx_openid_location=' . rawurlencode($requestURL) . '&' . 00440 'tx_openid_mode=finish&' . 00441 'tx_openid_claimed=' . rawurlencode($claimedIdentifier) . '&' . 00442 'tx_openid_signature=' . $this->getSignature($claimedIdentifier); 00443 return t3lib_div::locationHeaderUrl($returnURL); 00444 } 00445 00446 /** 00447 * Signs claimed id. 00448 * 00449 * @return void 00450 */ 00451 protected function getSignature($claimedIdentifier) { 00452 // You can also increase security by using sha1 (beware of too long URLs!) 00453 return md5(implode('/', array( 00454 $claimedIdentifier, 00455 strval(strlen($claimedIdentifier)), 00456 $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'] 00457 ))); 00458 } 00459 00460 /** 00461 * Calculates the path to the TYPO3 directory from the current directory 00462 * 00463 * @return string 00464 */ 00465 protected function getBackPath() { 00466 $extPath = t3lib_extMgm::siteRelPath('openid'); 00467 $segmentCount = count(explode('/', $extPath)); 00468 $path = str_pad('', $segmentCount*3, '../') . TYPO3_mainDir; 00469 00470 return $path; 00471 } 00472 00473 /** 00474 * Obtains a real identifier for the user 00475 * 00476 * @return string 00477 */ 00478 protected function getFinalOpenIDIdentifier() { 00479 $result = $this->getSignedParameter('openid_identity'); 00480 if (!$result) { 00481 $result = $this->getSignedParameter('openid_claimed_id'); 00482 } 00483 if (!$result) { 00484 $result = $this->getSignedClaimedOpenIDIdentifier(); 00485 } 00486 $result = $this->getAdjustedOpenIDIdentifier($result); 00487 return $result; 00488 } 00489 00490 /** 00491 * Gets the signed OpenID that was sent back to this service. 00492 * 00493 * @return string The signed OpenID, if signature did not match this is empty 00494 */ 00495 protected function getSignedClaimedOpenIDIdentifier() { 00496 $result = t3lib_div::_GP('tx_openid_claimed'); 00497 $signature = $this->getSignature($result); 00498 if ($signature !== t3lib_div::_GP('tx_openid_signature')) { 00499 $result = ''; 00500 } 00501 return $result; 00502 } 00503 00504 /** 00505 * Adjusts the OpenID identifier to to claimed OpenID, if the only difference 00506 * is in normalizing the URLs. Example: 00507 * + OpenID returned from provider: https://account.provider.net/ 00508 * + OpenID used in TYPO3: https://account.provider.net (not normalized) 00509 * 00510 * @param string $openIDIdentifier The OpenID returned by the OpenID provider 00511 * @return string Adjusted OpenID identifier 00512 */ 00513 protected function getAdjustedOpenIDIdentifier($openIDIdentifier) { 00514 $result = ''; 00515 00516 $claimedOpenIDIdentifier = $this->getSignedClaimedOpenIDIdentifier(); 00517 $pattern = '#^' . preg_quote($claimedOpenIDIdentifier, '#') . '/?$#'; 00518 00519 if (preg_match($pattern, $openIDIdentifier)) { 00520 $result = $claimedOpenIDIdentifier; 00521 } 00522 00523 return $result; 00524 } 00525 00526 /** 00527 * Obtains a value of the parameter if it is signed. If not signed, then 00528 * empty string is returned. 00529 * 00530 * @param string $parameterName Must start with 'openid_' 00531 * @return string 00532 */ 00533 protected function getSignedParameter($parameterName) { 00534 $signedParametersList = t3lib_div::_GP('openid_signed'); 00535 if (t3lib_div::inList($signedParametersList, substr($parameterName, 7))) { 00536 $result = t3lib_div::_GP($parameterName); 00537 } else { 00538 $result = ''; 00539 } 00540 return $result; 00541 } 00542 00543 /** 00544 * Writes log message. Destination log depends on the current system mode. 00545 * For FE the function writes to the admin panel log. For BE messages are 00546 * sent to the system log. If developer log is enabled, messages are also 00547 * sent there. 00548 * 00549 * This function accepts variable number of arguments and can format 00550 * parameters. The syntax is the same as for sprintf() 00551 * 00552 * @param string $message Message to output 00553 * @return void 00554 * @see sprintf() 00555 * @see t3lib::divLog() 00556 * @see t3lib_div::sysLog() 00557 * @see t3lib_timeTrack::setTSlogMessage() 00558 */ 00559 protected function writeLog($message) { 00560 if (func_num_args() > 1) { 00561 $params = func_get_args(); 00562 array_shift($params); 00563 $message = vsprintf($message, $params); 00564 } 00565 if (TYPO3_MODE == 'BE') { 00566 t3lib_div::sysLog($message, $this->extKey, 1); 00567 } else { 00568 $GLOBALS['TT']->setTSlogMessage($message); 00569 } 00570 if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['enable_DLOG']) { 00571 t3lib_div::devLog($message, $this->extKey, 1); 00572 } 00573 } 00574 } 00575 00576 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/openid/sv1/class.tx_openid_sv1.php'])) { 00577 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/openid/sv1/class.tx_openid_sv1.php']); 00578 } 00579 00580 ?>
1.8.0