Updating Janrain OpenID auth library
Source: https://github.com/openid/php-openid
This commit is contained in:
parent
f01c478aab
commit
49b755912f
|
@ -374,42 +374,7 @@ class Auth_OpenID_Association {
|
|||
}
|
||||
|
||||
$calculated_sig = $this->getMessageSignature($message);
|
||||
|
||||
return $this->constantTimeCompare($calculated_sig, $sig);
|
||||
}
|
||||
|
||||
/**
|
||||
* String comparison function which will complete in a constant time
|
||||
* for strings of any given matching length, to help prevent an attacker
|
||||
* from distinguishing how much of a signature token they have guessed
|
||||
* correctly.
|
||||
*
|
||||
* For this usage, it's assumed that the length of the string is known,
|
||||
* so we may safely short-circuit on mismatched lengths which will be known
|
||||
* to be invalid by the attacker.
|
||||
*
|
||||
* http://lists.openid.net/pipermail/openid-security/2010-July/001156.html
|
||||
* http://rdist.root.org/2010/01/07/timing-independent-array-comparison/
|
||||
*/
|
||||
private function constantTimeCompare($a, $b)
|
||||
{
|
||||
$len = strlen($a);
|
||||
if (strlen($b) !== $len) {
|
||||
// Short-circuit on length mismatch; attackers will already know
|
||||
// the correct target length so this is safe.
|
||||
return false;
|
||||
}
|
||||
if ($len == 0) {
|
||||
// 0-length valid input shouldn't really happen. :)
|
||||
return true;
|
||||
}
|
||||
$result = 0;
|
||||
for ($i = 0; $i < strlen($a); $i++) {
|
||||
// We use scary bitwise operations to avoid logical short-circuits
|
||||
// in lower-level code.
|
||||
$result |= ord($a{$i}) ^ ord($b{$i});
|
||||
}
|
||||
return ($result == 0);
|
||||
return Auth_OpenID_CryptUtil::constEq($calculated_sig, $sig);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -365,7 +365,6 @@ function Auth_OpenID_detectMathLibrary($exts)
|
|||
{
|
||||
$loaded = false;
|
||||
|
||||
$hasDl = function_exists('dl');
|
||||
foreach ($exts as $extension) {
|
||||
if (extension_loaded($extension['extension'])) {
|
||||
return $extension;
|
||||
|
|
|
@ -957,6 +957,10 @@ class Auth_OpenID_GenericConsumer {
|
|||
}
|
||||
|
||||
if (!$assoc->checkMessageSignature($message)) {
|
||||
// If we get a "bad signature" here, it means that the association
|
||||
// is unrecoverabley corrupted in some way. Any futher attempts
|
||||
// to login with this association is likely to fail. Drop it.
|
||||
$this->store->removeAssociation($server_url, $assoc_handle);
|
||||
return new Auth_OpenID_FailureResponse(null,
|
||||
"Bad signature");
|
||||
}
|
||||
|
@ -1179,9 +1183,11 @@ class Auth_OpenID_GenericConsumer {
|
|||
function _discoverAndVerify($claimed_id, $to_match_endpoints)
|
||||
{
|
||||
// oidutil.log('Performing discovery on %s' % (claimed_id,))
|
||||
list($unused, $services) = call_user_func($this->discoverMethod,
|
||||
$claimed_id,
|
||||
&$this->fetcher);
|
||||
list($unused, $services) = call_user_func_array($this->discoverMethod,
|
||||
array(
|
||||
$claimed_id,
|
||||
&$this->fetcher,
|
||||
));
|
||||
|
||||
if (!$services) {
|
||||
return new Auth_OpenID_FailureResponse(null,
|
||||
|
|
|
@ -104,5 +104,19 @@ class Auth_OpenID_CryptUtil {
|
|||
|
||||
return $str;
|
||||
}
|
||||
|
||||
static function constEq($s1, $s2)
|
||||
{
|
||||
if (strlen($s1) != strlen($s2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = true;
|
||||
$length = strlen($s1);
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$result &= ($s1[$i] == $s2[$i]);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ class Auth_OpenID_Extension {
|
|||
*
|
||||
* Returns the message with the extension arguments added.
|
||||
*/
|
||||
function toMessage($message)
|
||||
function toMessage($message, $request = null)
|
||||
{
|
||||
$implicit = $message->isOpenID1();
|
||||
$added = $message->namespaces->addAlias($this->ns_uri,
|
||||
|
@ -53,8 +53,13 @@ class Auth_OpenID_Extension {
|
|||
}
|
||||
}
|
||||
|
||||
$message->updateArgs($this->ns_uri,
|
||||
$this->getExtensionArgs());
|
||||
if ($request !== null) {
|
||||
$message->updateArgs($this->ns_uri,
|
||||
$this->getExtensionArgs($request));
|
||||
} else {
|
||||
$message->updateArgs($this->ns_uri,
|
||||
$this->getExtensionArgs());
|
||||
}
|
||||
return $message;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -300,13 +300,22 @@ class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore {
|
|||
return null;
|
||||
}
|
||||
|
||||
if (file_exists($filename) !== true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$assoc_file = @fopen($filename, 'rb');
|
||||
|
||||
if ($assoc_file === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$assoc_s = fread($assoc_file, filesize($filename));
|
||||
$filesize = filesize($filename);
|
||||
if ($filesize === false || $filesize <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$assoc_s = fread($assoc_file, $filesize);
|
||||
fclose($assoc_file);
|
||||
|
||||
if (!$assoc_s) {
|
||||
|
|
|
@ -60,6 +60,13 @@ function Auth_OpenID_HMACSHA1($key, $text)
|
|||
$key = Auth_OpenID_SHA1($key, true);
|
||||
}
|
||||
|
||||
if (function_exists('hash_hmac') &&
|
||||
function_exists('hash_algos') &&
|
||||
(in_array('sha1', hash_algos()))) {
|
||||
return hash_hmac('sha1', $text, $key, true);
|
||||
}
|
||||
// Home-made solution
|
||||
|
||||
$key = str_pad($key, Auth_OpenID_SHA1_BLOCKSIZE, chr(0x00));
|
||||
$ipad = str_repeat(chr(0x36), Auth_OpenID_SHA1_BLOCKSIZE);
|
||||
$opad = str_repeat(chr(0x5c), Auth_OpenID_SHA1_BLOCKSIZE);
|
||||
|
|
413
extlib/Auth/OpenID/MDB2Store.php
Normal file
413
extlib/Auth/OpenID/MDB2Store.php
Normal file
|
@ -0,0 +1,413 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* SQL-backed OpenID stores for use with PEAR::MDB2.
|
||||
*
|
||||
* PHP versions 4 and 5
|
||||
*
|
||||
* LICENSE: See the COPYING file included in this distribution.
|
||||
*
|
||||
* @package OpenID
|
||||
* @author JanRain, Inc. <openid@janrain.com>
|
||||
* @copyright 2005 Janrain, Inc.
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL
|
||||
*/
|
||||
|
||||
require_once 'MDB2.php';
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
require_once 'Auth/OpenID/Interface.php';
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
require_once 'Auth/OpenID.php';
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
require_once 'Auth/OpenID/Nonce.php';
|
||||
|
||||
/**
|
||||
* This store uses a PEAR::MDB2 connection to store persistence
|
||||
* information.
|
||||
*
|
||||
* The table names used are determined by the class variables
|
||||
* associations_table_name and nonces_table_name. To change the name
|
||||
* of the tables used, pass new table names into the constructor.
|
||||
*
|
||||
* To create the tables with the proper schema, see the createTables
|
||||
* method.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
class Auth_OpenID_MDB2Store extends Auth_OpenID_OpenIDStore {
|
||||
/**
|
||||
* This creates a new MDB2Store instance. It requires an
|
||||
* established database connection be given to it, and it allows
|
||||
* overriding the default table names.
|
||||
*
|
||||
* @param connection $connection This must be an established
|
||||
* connection to a database of the correct type for the SQLStore
|
||||
* subclass you're using. This must be a PEAR::MDB2 connection
|
||||
* handle.
|
||||
*
|
||||
* @param associations_table: This is an optional parameter to
|
||||
* specify the name of the table used for storing associations.
|
||||
* The default value is 'oid_associations'.
|
||||
*
|
||||
* @param nonces_table: This is an optional parameter to specify
|
||||
* the name of the table used for storing nonces. The default
|
||||
* value is 'oid_nonces'.
|
||||
*/
|
||||
function Auth_OpenID_MDB2Store($connection,
|
||||
$associations_table = null,
|
||||
$nonces_table = null)
|
||||
{
|
||||
$this->associations_table_name = "oid_associations";
|
||||
$this->nonces_table_name = "oid_nonces";
|
||||
|
||||
// Check the connection object type to be sure it's a PEAR
|
||||
// database connection.
|
||||
if (!is_object($connection) ||
|
||||
!is_subclass_of($connection, 'mdb2_driver_common')) {
|
||||
trigger_error("Auth_OpenID_MDB2Store expected PEAR connection " .
|
||||
"object (got ".get_class($connection).")",
|
||||
E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->connection = $connection;
|
||||
|
||||
// Be sure to set the fetch mode so the results are keyed on
|
||||
// column name instead of column index.
|
||||
$this->connection->setFetchMode(MDB2_FETCHMODE_ASSOC);
|
||||
|
||||
if (@PEAR::isError($this->connection->loadModule('Extended'))) {
|
||||
trigger_error("Unable to load MDB2_Extended module", E_USER_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($associations_table) {
|
||||
$this->associations_table_name = $associations_table;
|
||||
}
|
||||
|
||||
if ($nonces_table) {
|
||||
$this->nonces_table_name = $nonces_table;
|
||||
}
|
||||
|
||||
$this->max_nonce_age = 6 * 60 * 60;
|
||||
}
|
||||
|
||||
function tableExists($table_name)
|
||||
{
|
||||
return !@PEAR::isError($this->connection->query(
|
||||
sprintf("SELECT * FROM %s LIMIT 0",
|
||||
$table_name)));
|
||||
}
|
||||
|
||||
function createTables()
|
||||
{
|
||||
$n = $this->create_nonce_table();
|
||||
$a = $this->create_assoc_table();
|
||||
|
||||
if (!$n || !$a) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function create_nonce_table()
|
||||
{
|
||||
if (!$this->tableExists($this->nonces_table_name)) {
|
||||
switch ($this->connection->phptype) {
|
||||
case "mysql":
|
||||
case "mysqli":
|
||||
// Custom SQL for MySQL to use InnoDB and variable-
|
||||
// length keys
|
||||
$r = $this->connection->exec(
|
||||
sprintf("CREATE TABLE %s (\n".
|
||||
" server_url VARCHAR(2047) NOT NULL DEFAULT '',\n".
|
||||
" timestamp INTEGER NOT NULL,\n".
|
||||
" salt CHAR(40) NOT NULL,\n".
|
||||
" UNIQUE (server_url(255), timestamp, salt)\n".
|
||||
") TYPE=InnoDB",
|
||||
$this->nonces_table_name));
|
||||
if (@PEAR::isError($r)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (@PEAR::isError(
|
||||
$this->connection->loadModule('Manager'))) {
|
||||
return false;
|
||||
}
|
||||
$fields = array(
|
||||
"server_url" => array(
|
||||
"type" => "text",
|
||||
"length" => 2047,
|
||||
"notnull" => true
|
||||
),
|
||||
"timestamp" => array(
|
||||
"type" => "integer",
|
||||
"notnull" => true
|
||||
),
|
||||
"salt" => array(
|
||||
"type" => "text",
|
||||
"length" => 40,
|
||||
"fixed" => true,
|
||||
"notnull" => true
|
||||
)
|
||||
);
|
||||
$constraint = array(
|
||||
"unique" => 1,
|
||||
"fields" => array(
|
||||
"server_url" => true,
|
||||
"timestamp" => true,
|
||||
"salt" => true
|
||||
)
|
||||
);
|
||||
|
||||
$r = $this->connection->createTable($this->nonces_table_name,
|
||||
$fields);
|
||||
if (@PEAR::isError($r)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$r = $this->connection->createConstraint(
|
||||
$this->nonces_table_name,
|
||||
$this->nonces_table_name . "_constraint",
|
||||
$constraint);
|
||||
if (@PEAR::isError($r)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function create_assoc_table()
|
||||
{
|
||||
if (!$this->tableExists($this->associations_table_name)) {
|
||||
switch ($this->connection->phptype) {
|
||||
case "mysql":
|
||||
case "mysqli":
|
||||
// Custom SQL for MySQL to use InnoDB and variable-
|
||||
// length keys
|
||||
$r = $this->connection->exec(
|
||||
sprintf("CREATE TABLE %s(\n".
|
||||
" server_url VARCHAR(2047) NOT NULL DEFAULT '',\n".
|
||||
" handle VARCHAR(255) NOT NULL,\n".
|
||||
" secret BLOB NOT NULL,\n".
|
||||
" issued INTEGER NOT NULL,\n".
|
||||
" lifetime INTEGER NOT NULL,\n".
|
||||
" assoc_type VARCHAR(64) NOT NULL,\n".
|
||||
" PRIMARY KEY (server_url(255), handle)\n".
|
||||
") TYPE=InnoDB",
|
||||
$this->associations_table_name));
|
||||
if (@PEAR::isError($r)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (@PEAR::isError(
|
||||
$this->connection->loadModule('Manager'))) {
|
||||
return false;
|
||||
}
|
||||
$fields = array(
|
||||
"server_url" => array(
|
||||
"type" => "text",
|
||||
"length" => 2047,
|
||||
"notnull" => true
|
||||
),
|
||||
"handle" => array(
|
||||
"type" => "text",
|
||||
"length" => 255,
|
||||
"notnull" => true
|
||||
),
|
||||
"secret" => array(
|
||||
"type" => "blob",
|
||||
"length" => "255",
|
||||
"notnull" => true
|
||||
),
|
||||
"issued" => array(
|
||||
"type" => "integer",
|
||||
"notnull" => true
|
||||
),
|
||||
"lifetime" => array(
|
||||
"type" => "integer",
|
||||
"notnull" => true
|
||||
),
|
||||
"assoc_type" => array(
|
||||
"type" => "text",
|
||||
"length" => 64,
|
||||
"notnull" => true
|
||||
)
|
||||
);
|
||||
$options = array(
|
||||
"primary" => array(
|
||||
"server_url" => true,
|
||||
"handle" => true
|
||||
)
|
||||
);
|
||||
|
||||
$r = $this->connection->createTable(
|
||||
$this->associations_table_name,
|
||||
$fields,
|
||||
$options);
|
||||
if (@PEAR::isError($r)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function storeAssociation($server_url, $association)
|
||||
{
|
||||
$fields = array(
|
||||
"server_url" => array(
|
||||
"value" => $server_url,
|
||||
"key" => true
|
||||
),
|
||||
"handle" => array(
|
||||
"value" => $association->handle,
|
||||
"key" => true
|
||||
),
|
||||
"secret" => array(
|
||||
"value" => $association->secret,
|
||||
"type" => "blob"
|
||||
),
|
||||
"issued" => array(
|
||||
"value" => $association->issued
|
||||
),
|
||||
"lifetime" => array(
|
||||
"value" => $association->lifetime
|
||||
),
|
||||
"assoc_type" => array(
|
||||
"value" => $association->assoc_type
|
||||
)
|
||||
);
|
||||
|
||||
return !@PEAR::isError($this->connection->replace(
|
||||
$this->associations_table_name,
|
||||
$fields));
|
||||
}
|
||||
|
||||
function cleanupNonces()
|
||||
{
|
||||
global $Auth_OpenID_SKEW;
|
||||
$v = time() - $Auth_OpenID_SKEW;
|
||||
|
||||
return $this->connection->exec(
|
||||
sprintf("DELETE FROM %s WHERE timestamp < %d",
|
||||
$this->nonces_table_name, $v));
|
||||
}
|
||||
|
||||
function cleanupAssociations()
|
||||
{
|
||||
return $this->connection->exec(
|
||||
sprintf("DELETE FROM %s WHERE issued + lifetime < %d",
|
||||
$this->associations_table_name, time()));
|
||||
}
|
||||
|
||||
function getAssociation($server_url, $handle = null)
|
||||
{
|
||||
$sql = "";
|
||||
$params = null;
|
||||
$types = array(
|
||||
"text",
|
||||
"blob",
|
||||
"integer",
|
||||
"integer",
|
||||
"text"
|
||||
);
|
||||
if ($handle !== null) {
|
||||
$sql = sprintf("SELECT handle, secret, issued, lifetime, assoc_type " .
|
||||
"FROM %s WHERE server_url = ? AND handle = ?",
|
||||
$this->associations_table_name);
|
||||
$params = array($server_url, $handle);
|
||||
} else {
|
||||
$sql = sprintf("SELECT handle, secret, issued, lifetime, assoc_type " .
|
||||
"FROM %s WHERE server_url = ? ORDER BY issued DESC",
|
||||
$this->associations_table_name);
|
||||
$params = array($server_url);
|
||||
}
|
||||
|
||||
$assoc = $this->connection->getRow($sql, $types, $params);
|
||||
|
||||
if (!$assoc || @PEAR::isError($assoc)) {
|
||||
return null;
|
||||
} else {
|
||||
$association = new Auth_OpenID_Association($assoc['handle'],
|
||||
stream_get_contents(
|
||||
$assoc['secret']),
|
||||
$assoc['issued'],
|
||||
$assoc['lifetime'],
|
||||
$assoc['assoc_type']);
|
||||
fclose($assoc['secret']);
|
||||
return $association;
|
||||
}
|
||||
}
|
||||
|
||||
function removeAssociation($server_url, $handle)
|
||||
{
|
||||
$r = $this->connection->execParam(
|
||||
sprintf("DELETE FROM %s WHERE server_url = ? AND handle = ?",
|
||||
$this->associations_table_name),
|
||||
array($server_url, $handle));
|
||||
|
||||
if (@PEAR::isError($r) || $r == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function useNonce($server_url, $timestamp, $salt)
|
||||
{
|
||||
global $Auth_OpenID_SKEW;
|
||||
|
||||
if (abs($timestamp - time()) > $Auth_OpenID_SKEW ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$fields = array(
|
||||
"timestamp" => $timestamp,
|
||||
"salt" => $salt
|
||||
);
|
||||
|
||||
if (!empty($server_url)) {
|
||||
$fields["server_url"] = $server_url;
|
||||
}
|
||||
|
||||
$r = $this->connection->autoExecute(
|
||||
$this->nonces_table_name,
|
||||
$fields,
|
||||
MDB2_AUTOQUERY_INSERT);
|
||||
|
||||
if (@PEAR::isError($r)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the store by removing all records from the store's
|
||||
* tables.
|
||||
*/
|
||||
function reset()
|
||||
{
|
||||
$this->connection->query(sprintf("DELETE FROM %s",
|
||||
$this->associations_table_name));
|
||||
|
||||
$this->connection->query(sprintf("DELETE FROM %s",
|
||||
$this->nonces_table_name));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -675,7 +675,7 @@ class Auth_OpenID_Message {
|
|||
|
||||
if ($form_tag_attrs) {
|
||||
foreach ($form_tag_attrs as $name => $attr) {
|
||||
$form .= sprintf(" %s=\"%s\"", $name, $attr);
|
||||
$form .= sprintf(" %s=\"%s\"", $name, htmlspecialchars($attr));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -684,11 +684,11 @@ class Auth_OpenID_Message {
|
|||
foreach ($this->toPostArgs() as $name => $value) {
|
||||
$form .= sprintf(
|
||||
"<input type=\"hidden\" name=\"%s\" value=\"%s\" />\n",
|
||||
$name, $value);
|
||||
htmlspecialchars($name), htmlspecialchars($value));
|
||||
}
|
||||
|
||||
$form .= sprintf("<input type=\"submit\" value=\"%s\" />\n",
|
||||
$submit_text);
|
||||
htmlspecialchars($submit_text));
|
||||
|
||||
$form .= "</form>\n";
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ class Auth_OpenID_MySQLStore extends Auth_OpenID_SQLStore {
|
|||
|
||||
$this->sql['assoc_table'] =
|
||||
"CREATE TABLE %s (\n".
|
||||
" server_url BLOB NOT NULL,\n".
|
||||
" server_url VARCHAR(2047) NOT NULL,\n".
|
||||
" handle VARCHAR(255) NOT NULL,\n".
|
||||
" secret BLOB NOT NULL,\n".
|
||||
" issued INTEGER NOT NULL,\n".
|
||||
|
|
|
@ -219,7 +219,11 @@ class Auth_OpenID_Parse {
|
|||
function match($regexp, $text, &$match)
|
||||
{
|
||||
if (!is_callable('mb_ereg_search_init')) {
|
||||
return preg_match($regexp, $text, $match);
|
||||
if (!preg_match($regexp, $text, $match)) {
|
||||
return false;
|
||||
}
|
||||
$match = $match[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
$regexp = substr($regexp, 1, strlen($regexp) - 2 - strlen($this->_re_flags));
|
||||
|
@ -227,7 +231,7 @@ class Auth_OpenID_Parse {
|
|||
if (!mb_ereg_search($regexp)) {
|
||||
return false;
|
||||
}
|
||||
list($match) = mb_ereg_search_getregs();
|
||||
$match = mb_ereg_search_getregs();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -269,7 +273,7 @@ class Auth_OpenID_Parse {
|
|||
|
||||
// Try to find the <HEAD> tag.
|
||||
$head_re = $this->headFind();
|
||||
$head_match = '';
|
||||
$head_match = array();
|
||||
if (!$this->match($head_re, $stripped, $head_match)) {
|
||||
ini_set( 'pcre.backtrack_limit', $old_btlimit );
|
||||
return array();
|
||||
|
@ -278,7 +282,7 @@ class Auth_OpenID_Parse {
|
|||
$link_data = array();
|
||||
$link_matches = array();
|
||||
|
||||
if (!preg_match_all($this->_link_find, $head_match,
|
||||
if (!preg_match_all($this->_link_find, $head_match[0],
|
||||
$link_matches)) {
|
||||
ini_set( 'pcre.backtrack_limit', $old_btlimit );
|
||||
return array();
|
||||
|
|
208
extlib/Auth/OpenID/PredisStore.php
Normal file
208
extlib/Auth/OpenID/PredisStore.php
Normal file
|
@ -0,0 +1,208 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Supplies Redis server store backend for OpenID servers and consumers.
|
||||
* Uses Predis library {@see https://github.com/nrk/predis}.
|
||||
* Requires PHP >= 5.3.
|
||||
*
|
||||
* LICENSE: See the COPYING file included in this distribution.
|
||||
*
|
||||
* @package OpenID
|
||||
* @author Ville Mattila <ville@eventio.fi>
|
||||
* @copyright 2008 JanRain Inc., 2013 Eventio Oy / Ville Mattila
|
||||
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
|
||||
* Contributed by Eventio Oy <http://www.eventio.fi/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Import the interface for creating a new store class.
|
||||
*/
|
||||
require_once 'Auth/OpenID/Interface.php';
|
||||
|
||||
/**
|
||||
* Supplies Redis server store backend for OpenID servers and consumers.
|
||||
* Uses Predis library {@see https://github.com/nrk/predis}.
|
||||
* Requires PHP >= 5.3.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
class Auth_OpenID_PredisStore extends Auth_OpenID_OpenIDStore {
|
||||
|
||||
/**
|
||||
* @var \Predis\Client
|
||||
*/
|
||||
protected $redis;
|
||||
|
||||
/**
|
||||
* Prefix for Redis keys
|
||||
* @var string
|
||||
*/
|
||||
protected $prefix;
|
||||
|
||||
/**
|
||||
* Initializes a new {@link Auth_OpenID_PredisStore} instance.
|
||||
*
|
||||
* @param \Predis\Client $redis Predis client object
|
||||
* @param string $prefix Prefix for all keys stored to the Redis
|
||||
*/
|
||||
function Auth_OpenID_PredisStore(\Predis\Client $redis, $prefix = '')
|
||||
{
|
||||
$this->prefix = $prefix;
|
||||
$this->redis = $redis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store association until its expiration time in Redis server.
|
||||
* Overwrites any existing association with same server_url and
|
||||
* handle. Handles list of associations for every server.
|
||||
*/
|
||||
function storeAssociation($server_url, $association)
|
||||
{
|
||||
// create Redis keys for association itself
|
||||
// and list of associations for this server
|
||||
$associationKey = $this->associationKey($server_url,
|
||||
$association->handle);
|
||||
$serverKey = $this->associationServerKey($server_url);
|
||||
|
||||
// save association to server's associations' keys list
|
||||
$this->redis->lpush(
|
||||
$serverKey,
|
||||
$associationKey
|
||||
);
|
||||
|
||||
// Will touch the association list expiration, to avoid filling up
|
||||
$newExpiration = ($association->issued + $association->lifetime);
|
||||
|
||||
$expirationKey = $serverKey.'_expires_at';
|
||||
$expiration = $this->redis->get($expirationKey);
|
||||
if (!$expiration || $newExpiration > $expiration) {
|
||||
$this->redis->set($expirationKey, $newExpiration);
|
||||
$this->redis->expireat($serverKey, $newExpiration);
|
||||
$this->redis->expireat($expirationKey, $newExpiration);
|
||||
}
|
||||
|
||||
// save association itself, will automatically expire
|
||||
$this->redis->setex(
|
||||
$associationKey,
|
||||
$newExpiration - time(),
|
||||
serialize($association)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read association from Redis. If no handle given
|
||||
* and multiple associations found, returns latest issued
|
||||
*/
|
||||
function getAssociation($server_url, $handle = null)
|
||||
{
|
||||
// simple case: handle given
|
||||
if ($handle !== null) {
|
||||
return $this->getAssociationFromServer(
|
||||
$this->associationKey($server_url, $handle)
|
||||
);
|
||||
}
|
||||
|
||||
// no handle given, receiving the latest issued
|
||||
$serverKey = $this->associationServerKey($server_url);
|
||||
$lastKey = $this->redis->lpop($serverKey);
|
||||
if (!$lastKey) { return null; }
|
||||
|
||||
// get association, return null if failed
|
||||
return $this->getAssociationFromServer($lastKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to actually receive and unserialize the association
|
||||
* from the server.
|
||||
*/
|
||||
private function getAssociationFromServer($associationKey)
|
||||
{
|
||||
$association = $this->redis->get($associationKey);
|
||||
return $association ? unserialize($association) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Immediately delete association from Redis.
|
||||
*/
|
||||
function removeAssociation($server_url, $handle)
|
||||
{
|
||||
// create Redis keys
|
||||
$serverKey = $this->associationServerKey($server_url);
|
||||
$associationKey = $this->associationKey($server_url,
|
||||
$handle);
|
||||
|
||||
// Removing the association from the server's association list
|
||||
$removed = $this->redis->lrem($serverKey, 0, $associationKey);
|
||||
if ($removed < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Delete the association itself
|
||||
return $this->redis->del($associationKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create nonce for server and salt, expiring after
|
||||
* $Auth_OpenID_SKEW seconds.
|
||||
*/
|
||||
function useNonce($server_url, $timestamp, $salt)
|
||||
{
|
||||
global $Auth_OpenID_SKEW;
|
||||
|
||||
// save one request to memcache when nonce obviously expired
|
||||
if (abs($timestamp - time()) > $Auth_OpenID_SKEW) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// SETNX will set the value only of the key doesn't exist yet.
|
||||
$nonceKey = $this->nonceKey($server_url, $salt);
|
||||
$added = $this->predis->setnx($nonceKey);
|
||||
if ($added) {
|
||||
// Will set expiration
|
||||
$this->predis->expire($nonceKey, $Auth_OpenID_SKEW);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build up nonce key
|
||||
*/
|
||||
private function nonceKey($server_url, $salt)
|
||||
{
|
||||
return $this->prefix .
|
||||
'openid_nonce_' .
|
||||
sha1($server_url) . '_' . sha1($salt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Key is prefixed with $prefix and 'openid_association_' string
|
||||
*/
|
||||
function associationKey($server_url, $handle = null)
|
||||
{
|
||||
return $this->prefix .
|
||||
'openid_association_' .
|
||||
sha1($server_url) . '_' . sha1($handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Key is prefixed with $prefix and 'openid_association_server_' string
|
||||
*/
|
||||
function associationServerKey($server_url)
|
||||
{
|
||||
return $this->prefix .
|
||||
'openid_association_server_' .
|
||||
sha1($server_url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Report that this storage doesn't support cleanup
|
||||
*/
|
||||
function supportsCleanup()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -166,7 +166,7 @@ class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore {
|
|||
*/
|
||||
function isError($value)
|
||||
{
|
||||
return PEAR::isError($value);
|
||||
return @PEAR::isError($value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -817,11 +817,11 @@ class Auth_OpenID_CheckIDRequest extends Auth_OpenID_Request {
|
|||
*/
|
||||
function returnToVerified()
|
||||
{
|
||||
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
|
||||
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
|
||||
return call_user_func_array($this->verifyReturnTo,
|
||||
array($this->trust_root, $this->return_to, $fetcher));
|
||||
}
|
||||
|
||||
|
||||
static function fromMessage($message, $server)
|
||||
{
|
||||
$mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode');
|
||||
|
@ -1704,7 +1704,7 @@ class Auth_OpenID_Server {
|
|||
{
|
||||
if (method_exists($this, "openid_" . $request->mode)) {
|
||||
$handler = array($this, "openid_" . $request->mode);
|
||||
return call_user_func($handler, &$request);
|
||||
return call_user_func_array($handler, array($request));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ class Auth_Yadis_PHPSession {
|
|||
*/
|
||||
function get($name, $default=null)
|
||||
{
|
||||
if (array_key_exists($name, $_SESSION)) {
|
||||
if (isset($_SESSION) && array_key_exists($name, $_SESSION)) {
|
||||
return $_SESSION[$name];
|
||||
} else {
|
||||
return $default;
|
||||
|
@ -411,9 +411,11 @@ class Auth_Yadis_Discovery {
|
|||
if (!$manager || (!$manager->services)) {
|
||||
$this->destroyManager();
|
||||
|
||||
list($yadis_url, $services) = call_user_func($discover_cb,
|
||||
$this->url,
|
||||
&$fetcher);
|
||||
list($yadis_url, $services) = call_user_func_array($discover_cb,
|
||||
array(
|
||||
$this->url,
|
||||
&$fetcher,
|
||||
));
|
||||
|
||||
$manager = $this->createManager($services, $yadis_url);
|
||||
}
|
||||
|
|
|
@ -129,8 +129,20 @@ class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
|
|||
curl_setopt($c, CURLOPT_URL, $url);
|
||||
|
||||
if (defined('Auth_OpenID_VERIFY_HOST')) {
|
||||
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true);
|
||||
curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
|
||||
// set SSL verification options only if Auth_OpenID_VERIFY_HOST
|
||||
// is explicitly set, otherwise use system default.
|
||||
if (Auth_OpenID_VERIFY_HOST) {
|
||||
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true);
|
||||
curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
|
||||
if (defined('Auth_OpenID_CAINFO')) {
|
||||
curl_setopt($c, CURLOPT_CAINFO, Auth_OpenID_CAINFO);
|
||||
}
|
||||
} else {
|
||||
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
|
||||
}
|
||||
}
|
||||
if (defined('Auth_OpenID_HTTP_PROXY')) {
|
||||
curl_setopt($c, CURLOPT_PROXY, Auth_OpenID_HTTP_PROXY);
|
||||
}
|
||||
curl_exec($c);
|
||||
|
||||
|
@ -153,6 +165,7 @@ class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
|
|||
curl_close($c);
|
||||
|
||||
if (defined('Auth_OpenID_VERIFY_HOST') &&
|
||||
Auth_OpenID_VERIFY_HOST == true &&
|
||||
$this->isHTTPS($url)) {
|
||||
Auth_OpenID::log('OpenID: Verified SSL host %s using '.
|
||||
'curl/get', $url);
|
||||
|
@ -166,10 +179,6 @@ class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
|
|||
}
|
||||
}
|
||||
|
||||
Auth_OpenID::log(
|
||||
"Successfully fetched '%s': GET response code %s",
|
||||
$url, $code);
|
||||
|
||||
return new Auth_Yadis_HTTPResponse($url, $code,
|
||||
$new_headers, $body);
|
||||
}
|
||||
|
@ -194,6 +203,10 @@ class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
|
|||
curl_setopt($c, CURLOPT_NOSIGNAL, true);
|
||||
}
|
||||
|
||||
if (defined('Auth_OpenID_HTTP_PROXY')) {
|
||||
curl_setopt($c, CURLOPT_PROXY, Auth_OpenID_HTTP_PROXY);
|
||||
}
|
||||
|
||||
curl_setopt($c, CURLOPT_POST, true);
|
||||
curl_setopt($c, CURLOPT_POSTFIELDS, $body);
|
||||
curl_setopt($c, CURLOPT_TIMEOUT, $this->timeout);
|
||||
|
@ -202,8 +215,17 @@ class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
|
|||
array($this, "_writeData"));
|
||||
|
||||
if (defined('Auth_OpenID_VERIFY_HOST')) {
|
||||
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true);
|
||||
curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
|
||||
// set SSL verification options only if Auth_OpenID_VERIFY_HOST
|
||||
// is explicitly set, otherwise use system default.
|
||||
if (Auth_OpenID_VERIFY_HOST) {
|
||||
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, true);
|
||||
curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2);
|
||||
if (defined('Auth_OpenID_CAINFO')) {
|
||||
curl_setopt($c, CURLOPT_CAINFO, Auth_OpenID_CAINFO);
|
||||
}
|
||||
} else {
|
||||
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false);
|
||||
}
|
||||
}
|
||||
|
||||
curl_exec($c);
|
||||
|
@ -217,7 +239,9 @@ class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
|
|||
return null;
|
||||
}
|
||||
|
||||
if (defined('Auth_OpenID_VERIFY_HOST') && $this->isHTTPS($url)) {
|
||||
if (defined('Auth_OpenID_VERIFY_HOST') &&
|
||||
Auth_OpenID_VERIFY_HOST == true &&
|
||||
$this->isHTTPS($url)) {
|
||||
Auth_OpenID::log('OpenID: Verified SSL host %s using '.
|
||||
'curl/post', $url);
|
||||
}
|
||||
|
@ -235,9 +259,6 @@ class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
|
|||
|
||||
}
|
||||
|
||||
Auth_OpenID::log("Successfully fetched '%s': POST response code %s",
|
||||
$url, $code);
|
||||
|
||||
return new Auth_Yadis_HTTPResponse($url, $code,
|
||||
$new_headers, $body);
|
||||
}
|
||||
|
|
|
@ -234,7 +234,19 @@ class Auth_Yadis_dom extends Auth_Yadis_XMLParser {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!@$this->doc->loadXML($xml_string)) {
|
||||
// libxml_disable_entity_loader (PHP 5 >= 5.2.11)
|
||||
if (function_exists('libxml_disable_entity_loader') && function_exists('libxml_use_internal_errors')) {
|
||||
// disable external entities and libxml errors
|
||||
$loader = libxml_disable_entity_loader(true);
|
||||
$errors = libxml_use_internal_errors(true);
|
||||
$parse_result = @$this->doc->loadXML($xml_string);
|
||||
libxml_disable_entity_loader($loader);
|
||||
libxml_use_internal_errors($errors);
|
||||
} else {
|
||||
$parse_result = @$this->doc->loadXML($xml_string);
|
||||
}
|
||||
|
||||
if (!$parse_result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user