plugin_openid:

* New PHP-OpenId lib
* Patched it with a pull request found at GitHub solving depreated
problems
* A lot of fixes.
- Better documentation still missing, this will be the next commit.
This commit is contained in:
Grischa Brockhaus 2012-03-22 04:19:23 +01:00
parent 6cc37d8f54
commit 6a939ae1a9
57 changed files with 9506 additions and 3359 deletions

View file

@ -0,0 +1,6 @@
Version 0.3 (brockhaus)
---------------------------------
* New PHP-OpenId lib
* Patched it with a pull request found at GitHub solving depreated
problems
* A lot of fixes.

View file

@ -13,16 +13,22 @@
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* The library version string
*/
define('Auth_OpenID_VERSION', '2.2.2');
/**
* Require the fetcher code.
*/
require_once "Services/Yadis/PlainHTTPFetcher.php";
require_once "Services/Yadis/ParanoidHTTPFetcher.php";
require_once "Auth/Yadis/PlainHTTPFetcher.php";
require_once "Auth/Yadis/ParanoidHTTPFetcher.php";
require_once "Auth/OpenID/BigMath.php";
require_once "Auth/OpenID/URINorm.php";
/**
* Status code returned by the server when the only option is to show
@ -96,9 +102,7 @@ define('Auth_OpenID_digits',
define('Auth_OpenID_punct',
"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~");
if (Auth_OpenID_getMathLib() === null) {
define('Auth_OpenID_NO_MATH_SUPPORT', true);
}
Auth_OpenID_include_init();
/**
* The OpenID utility function class.
@ -109,41 +113,88 @@ if (Auth_OpenID_getMathLib() === null) {
class Auth_OpenID {
/**
* These namespaces are automatically fixed in query arguments by
* Auth_OpenID::fixArgs.
* Return true if $thing is an Auth_OpenID_FailureResponse object;
* false if not.
*
* @access private
*/
function getOpenIDNamespaces()
static function isFailure($thing)
{
return array('openid',
'sreg');
return is_a($thing, 'Auth_OpenID_FailureResponse');
}
/**
* Rename query arguments back to 'openid.' from 'openid_'
* Gets the query data from the server environment based on the
* request method used. If GET was used, this looks at
* $_SERVER['QUERY_STRING'] directly. If POST was used, this
* fetches data from the special php://input file stream.
*
* Returns an associative array of the query arguments.
*
* Skips invalid key/value pairs (i.e. keys with no '=value'
* portion).
*
* Returns an empty array if neither GET nor POST was used, or if
* POST was used but php://input cannot be opened.
*
* See background:
* http://lists.openidenabled.com/pipermail/dev/2007-March/000395.html
*
* @access private
* @param array $args An associative array of URL query arguments
*/
function fixArgs($args)
static function getQuery($query_str=null)
{
foreach (array_keys($args) as $key) {
$fixed = $key;
if (preg_match('/^openid/', $key)) {
foreach (Auth_OpenID::getOpenIDNamespaces() as $ns) {
if (preg_match('/'.$ns.'_/', $key)) {
$fixed = preg_replace('/'.$ns.'_/', $ns.'.', $fixed);
}
}
$data = array();
if ($fixed != $key) {
$val = $args[$key];
unset($args[$key]);
$args[$fixed] = $val;
}
if ($query_str !== null) {
$data = Auth_OpenID::params_from_string($query_str);
} else if (!array_key_exists('REQUEST_METHOD', $_SERVER)) {
// Do nothing.
} else {
// XXX HACK FIXME HORRIBLE.
//
// POSTing to a URL with query parameters is acceptable, but
// we don't have a clean way to distinguish those parameters
// when we need to do things like return_to verification
// which only want to look at one kind of parameter. We're
// going to emulate the behavior of some other environments
// by defaulting to GET and overwriting with POST if POST
// data is available.
$data = Auth_OpenID::params_from_string($_SERVER['QUERY_STRING']);
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$str = file_get_contents('php://input');
if ($str === false) {
$post = array();
} else {
$post = Auth_OpenID::params_from_string($str);
}
$data = array_merge($data, $post);
}
}
return $args;
return $data;
}
static function params_from_string($str)
{
$chunks = explode("&", $str);
$data = array();
foreach ($chunks as $chunk) {
$parts = explode("=", $chunk, 2);
if (count($parts) != 2) {
continue;
}
list($k, $v) = $parts;
$data[urldecode($k)] = urldecode($v);
}
return $data;
}
/**
@ -153,25 +204,45 @@ class Auth_OpenID {
*
* @access private
*/
function ensureDir($dir_name)
static function ensureDir($dir_name)
{
if (is_dir($dir_name) || @mkdir($dir_name)) {
return true;
} else {
if (Auth_OpenID::ensureDir(dirname($dir_name))) {
return is_dir($dir_name) || @mkdir($dir_name);
} else {
return false;
$parent_dir = dirname($dir_name);
// Terminal case; there is no parent directory to create.
if ($parent_dir == $dir_name) {
return true;
}
return (Auth_OpenID::ensureDir($parent_dir) && @mkdir($dir_name));
}
}
/**
* Convenience function for getting array values.
* Adds a string prefix to all values of an array. Returns a new
* array containing the prefixed values.
*
* @access private
*/
function arrayGet($arr, $key, $fallback = null)
static function addPrefix($values, $prefix)
{
$new_values = array();
foreach ($values as $s) {
$new_values[] = $prefix . $s;
}
return $new_values;
}
/**
* Convenience function for getting array values. Given an array
* $arr and a key $key, get the corresponding value from the array
* or return $default if the key is absent.
*
* @access private
*/
static function arrayGet($arr, $key, $fallback = null)
{
if (is_array($arr)) {
if (array_key_exists($key, $arr)) {
@ -180,12 +251,40 @@ class Auth_OpenID {
return $fallback;
}
} else {
trigger_error("Auth_OpenID::arrayGet expected " .
"array as first parameter", E_USER_WARNING);
trigger_error("Auth_OpenID::arrayGet (key = ".$key.") expected " .
"array as first parameter, got " .
gettype($arr), E_USER_WARNING);
return false;
}
}
/**
* Replacement for PHP's broken parse_str.
*/
static function parse_str($query)
{
if ($query === null) {
return null;
}
$parts = explode('&', $query);
$new_parts = array();
for ($i = 0; $i < count($parts); $i++) {
$pair = explode('=', $parts[$i]);
if (count($pair) != 2) {
continue;
}
list($key, $value) = $pair;
$new_parts[urldecode($key)] = urldecode($value);
}
return $new_parts;
}
/**
* Implements the PHP 5 'http_build_query' functionality.
*
@ -197,7 +296,7 @@ class Auth_OpenID {
* pairs from $data into a URL query string
* (e.g. "username=bob&id=56").
*/
function httpBuildQuery($data)
static function httpBuildQuery($data)
{
$pairs = array();
foreach ($data as $key => $value) {
@ -214,6 +313,7 @@ class Auth_OpenID {
* "Appends" query arguments onto a URL. The URL may or may not
* already have arguments (following a question mark).
*
* @access private
* @param string $url A URL, which may or may not already have
* arguments.
* @param array $args Either an array key/value pairs or an array of
@ -224,7 +324,7 @@ class Auth_OpenID {
* @return string $url The original URL with the new parameters added.
*
*/
function appendArgs($url, $args)
static function appendArgs($url, $args)
{
if (count($args) == 0) {
return $url;
@ -253,38 +353,6 @@ class Auth_OpenID {
return $url . $sep . Auth_OpenID::httpBuildQuery($args);
}
/**
* Turn a string into an ASCII string.
*
* Replace non-ascii characters with a %-encoded, UTF-8
* encoding. This function will fail if the input is a string and
* there are non-7-bit-safe characters. It is assumed that the
* caller will have already translated the input into a Unicode
* character sequence, according to the encoding of the HTTP POST
* or GET.
*
* Do not escape anything that is already 7-bit safe, so we do the
* minimal transform on the identity URL
*
* @access private
*/
function quoteMinimal($s)
{
$res = array();
for ($i = 0; $i < strlen($s); $i++) {
$c = $s[$i];
if ($c >= "\x80") {
for ($j = 0; $j < count(utf8_encode($c)); $j++) {
array_push($res, sprintf("%02X", ord($c[$j])));
}
} else {
array_push($res, $c);
}
}
return implode('', $res);
}
/**
* Implements python's urlunparse, which is not available in PHP.
* Given the specified components of a URL, this function rebuilds
@ -300,8 +368,8 @@ class Auth_OpenID {
* @return string $url The URL resulting from assembling the
* specified components.
*/
function urlunparse($scheme, $host, $port = null, $path = '/',
$query = '', $fragment = '')
static function urlunparse($scheme, $host, $port = null, $path = '/',
$query = '', $fragment = '')
{
if (!$scheme) {
@ -313,7 +381,7 @@ class Auth_OpenID {
}
if (!$path) {
$path = '/';
$path = '';
}
$result = $scheme . "://" . $host;
@ -345,68 +413,151 @@ class Auth_OpenID {
* @return mixed $new_url The URL after normalization, or null if
* $url was malformed.
*/
function normalizeUrl($url)
static function normalizeUrl($url)
{
if ($url === null) {
@$parsed = parse_url($url);
if (!$parsed) {
return null;
}
assert(is_string($url));
$old_url = $url;
$url = trim($url);
if (strpos($url, "://") === false) {
$url = "http://" . $url;
}
$parsed = @parse_url($url);
if ($parsed === false) {
return null;
}
$defaults = array(
'scheme' => '',
'host' => '',
'path' => '',
'query' => '',
'fragment' => '',
'port' => ''
);
$parsed = array_merge($defaults, $parsed);
if (($parsed['scheme'] == '') ||
($parsed['host'] == '')) {
if ($parsed['path'] == '' &&
$parsed['query'] == '' &&
$parsed['fragment'] == '') {
if (isset($parsed['scheme']) &&
isset($parsed['host'])) {
$scheme = strtolower($parsed['scheme']);
if (!in_array($scheme, array('http', 'https'))) {
return null;
}
$url = 'http://' + $url;
$parsed = parse_url($url);
$parsed = array_merge($defaults, $parsed);
} else {
$url = 'http://' . $url;
}
$tail = array_map(array('Auth_OpenID', 'quoteMinimal'),
array($parsed['path'],
$parsed['query'],
$parsed['fragment']));
if ($tail[0] == '') {
$tail[0] = '/';
$normalized = Auth_OpenID_urinorm($url);
if ($normalized === null) {
return null;
}
list($defragged, $frag) = Auth_OpenID::urldefrag($normalized);
return $defragged;
}
/**
* Replacement (wrapper) for PHP's intval() because it's broken.
*
* @access private
*/
static function intval($value)
{
$re = "/^\\d+$/";
if (!preg_match($re, $value)) {
return false;
}
$url = Auth_OpenID::urlunparse($parsed['scheme'], $parsed['host'],
$parsed['port'], $tail[0], $tail[1],
$tail[2]);
return intval($value);
}
assert(is_string($url));
/**
* Count the number of bytes in a string independently of
* multibyte support conditions.
*
* @param string $str The string of bytes to count.
* @return int The number of bytes in $str.
*/
static function bytes($str)
{
return strlen(bin2hex($str)) / 2;
}
return $url;
/**
* Get the bytes in a string independently of multibyte support
* conditions.
*/
static function toBytes($str)
{
$hex = bin2hex($str);
if (!$hex) {
return array();
}
$b = array();
for ($i = 0; $i < strlen($hex); $i += 2) {
$b[] = chr(base_convert(substr($hex, $i, 2), 16, 10));
}
return $b;
}
static function urldefrag($url)
{
$parts = explode("#", $url, 2);
if (count($parts) == 1) {
return array($parts[0], "");
} else {
return $parts;
}
}
static function filter($callback, &$sequence)
{
$result = array();
foreach ($sequence as $item) {
if (call_user_func_array($callback, array($item))) {
$result[] = $item;
}
}
return $result;
}
static function update(&$dest, &$src)
{
foreach ($src as $k => $v) {
$dest[$k] = $v;
}
}
/**
* Wrap PHP's standard error_log functionality. Use this to
* perform all logging. It will interpolate any additional
* arguments into the format string before logging.
*
* @param string $format_string The sprintf format for the message
*/
static function log($format_string)
{
$args = func_get_args();
$message = call_user_func_array('sprintf', $args);
error_log($message);
}
static function autoSubmitHTML($form, $title="OpenId transaction in progress")
{
return("<html>".
"<head><title>".
$title .
"</title></head>".
"<body onload='document.forms[0].submit();'>".
$form .
"<script>".
"var elements = document.forms[0].elements;".
"for (var i = 0; i < elements.length; i++) {".
" elements[i].style.display = \"none\";".
"}".
"</script>".
"</body>".
"</html>");
}
}
?>
/*
* Function to run when this file is included.
* Abstracted to a function to make life easier
* for some PHP optimizers.
*/
function Auth_OpenID_include_init() {
if (Auth_OpenID_getMathLib() === null) {
Auth_OpenID_setNoMathSupport();
}
}

File diff suppressed because it is too large Load diff

View file

@ -10,8 +10,8 @@
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
@ -24,6 +24,11 @@ require_once 'Auth/OpenID/CryptUtil.php';
*/
require_once 'Auth/OpenID/KVForm.php';
/**
* @access private
*/
require_once 'Auth/OpenID/HMAC.php';
/**
* This class represents an association between a server and a
* consumer. In general, users of this library will never see
@ -59,6 +64,11 @@ class Auth_OpenID_Association {
'assoc_type'
);
var $_macs = array(
'HMAC-SHA1' => 'Auth_OpenID_HMACSHA1',
'HMAC-SHA256' => 'Auth_OpenID_HMACSHA256'
);
/**
* This is an alternate constructor (factory method) used by the
* OpenID consumer library to create associations. OpenID store
@ -77,14 +87,14 @@ class Auth_OpenID_Association {
* generated for this association.
*
* @param assoc_type This is the type of association this
* instance represents. The only valid value of this field at
* this time is 'HMAC-SHA1', but new types may be defined in the
* future.
* instance represents. The only valid values of this field at
* this time is 'HMAC-SHA1' and 'HMAC-SHA256', but new types may
* be defined in the future.
*
* @return association An {@link Auth_OpenID_Association}
* instance.
*/
function fromExpiresIn($expires_in, $handle, $secret, $assoc_type)
static function fromExpiresIn($expires_in, $handle, $secret, $assoc_type)
{
$issued = time();
$lifetime = $expires_in;
@ -114,15 +124,16 @@ class Auth_OpenID_Association {
* association was issued.
*
* @param string $assoc_type This is the type of association this
* instance represents. The only valid value of this field at
* this time is 'HMAC-SHA1', but new types may be defined in the
* future.
* instance represents. The only valid values of this field at
* this time is 'HMAC-SHA1' and 'HMAC-SHA256', but new types may
* be defined in the future.
*/
function Auth_OpenID_Association(
$handle, $secret, $issued, $lifetime, $assoc_type)
{
if ($assoc_type != 'HMAC-SHA1') {
$fmt = 'HMAC-SHA1 is the only supported association type (got %s)';
if (!in_array($assoc_type,
Auth_OpenID_getSupportedAssociationTypes(), true)) {
$fmt = 'Unsupported association type (%s)';
trigger_error(sprintf($fmt, $assoc_type), E_USER_ERROR);
}
@ -195,7 +206,7 @@ class Auth_OpenID_Association {
* @param string $assoc_s Association as serialized by serialize()
* @return Auth_OpenID_Association $result instance of this class
*/
function deserialize($class_name, $assoc_s)
static function deserialize($class_name, $assoc_s)
{
$pairs = Auth_OpenID_KVForm::toArray($assoc_s, $strict = true);
$keys = array();
@ -215,7 +226,7 @@ class Auth_OpenID_Association {
sort($class_assoc_keys);
if ($keys != $class_assoc_keys) {
trigger_error('Unexpected key values: ' . strval($keys),
trigger_error('Unexpected key values: ' . var_export($keys, true),
E_USER_WARNING);
return null;
}
@ -252,7 +263,11 @@ class Auth_OpenID_Association {
function sign($pairs)
{
$kv = Auth_OpenID_KVForm::fromArray($pairs);
return Auth_OpenID_HMACSHA1($this->secret, $kv);
/* Invalid association types should be caught at constructor */
$callback = $this->_macs[$this->assoc_type];
return call_user_func_array($callback, array($this->secret, $kv));
}
/**
@ -265,44 +280,331 @@ class Auth_OpenID_Association {
* string => string pairs).
* @return string $signature The signature, base64 encoded
*/
function signDict($fields, $data, $prefix = 'openid.')
function signMessage($message)
{
$pairs = array();
foreach ($fields as $field) {
$pairs[] = array($field, $data[$prefix . $field]);
if ($message->hasKey(Auth_OpenID_OPENID_NS, 'sig') ||
$message->hasKey(Auth_OpenID_OPENID_NS, 'signed')) {
// Already has a sig
return null;
}
$extant_handle = $message->getArg(Auth_OpenID_OPENID_NS,
'assoc_handle');
if ($extant_handle && ($extant_handle != $this->handle)) {
// raise ValueError("Message has a different association handle")
return null;
}
$signed_message = $message;
$signed_message->setArg(Auth_OpenID_OPENID_NS, 'assoc_handle',
$this->handle);
$message_keys = array_keys($signed_message->toPostArgs());
$signed_list = array();
$signed_prefix = 'openid.';
foreach ($message_keys as $k) {
if (strpos($k, $signed_prefix) === 0) {
$signed_list[] = substr($k, strlen($signed_prefix));
}
}
$signed_list[] = 'signed';
sort($signed_list);
$signed_message->setArg(Auth_OpenID_OPENID_NS, 'signed',
implode(',', $signed_list));
$sig = $this->getMessageSignature($signed_message);
$signed_message->setArg(Auth_OpenID_OPENID_NS, 'sig', $sig);
return $signed_message;
}
/**
* Given a {@link Auth_OpenID_Message}, return the key/value pairs
* to be signed according to the signed list in the message. If
* the message lacks a signed list, return null.
*
* @access private
*/
function _makePairs($message)
{
$signed = $message->getArg(Auth_OpenID_OPENID_NS, 'signed');
if (!$signed || Auth_OpenID::isFailure($signed)) {
// raise ValueError('Message has no signed list: %s' % (message,))
return null;
}
$signed_list = explode(',', $signed);
$pairs = array();
$data = $message->toPostArgs();
foreach ($signed_list as $field) {
$pairs[] = array($field, Auth_OpenID::arrayGet($data,
'openid.' .
$field, ''));
}
return $pairs;
}
/**
* Given an {@link Auth_OpenID_Message}, return the signature for
* the signed list in the message.
*
* @access private
*/
function getMessageSignature($message)
{
$pairs = $this->_makePairs($message);
return base64_encode($this->sign($pairs));
}
/**
* Add a signature to an array of fields
*
* @access private
*/
function addSignature($fields, &$data, $prefix = 'openid.')
{
$sig = $this->signDict($fields, $data, $prefix);
$signed = implode(",", $fields);
$data[$prefix . 'sig'] = $sig;
$data[$prefix . 'signed'] = $signed;
}
/**
* Confirm that the signature of these fields matches the
* signature contained in the data
* signature contained in the data.
*
* @access private
*/
function checkSignature($data, $prefix = 'openid.')
function checkMessageSignature($message)
{
$signed = $data[$prefix . 'signed'];
$fields = explode(",", $signed);
$expected_sig = $this->signDict($fields, $data, $prefix);
$request_sig = $data[$prefix . 'sig'];
$sig = $message->getArg(Auth_OpenID_OPENID_NS,
'sig');
return ($request_sig == $expected_sig);
if (!$sig || Auth_OpenID::isFailure($sig)) {
return false;
}
$calculated_sig = $this->getMessageSignature($message);
return Auth_OpenID_CryptUtil::constEq($calculated_sig, $sig);
}
}
function Auth_OpenID_getSecretSize($assoc_type)
{
if ($assoc_type == 'HMAC-SHA1') {
return 20;
} else if ($assoc_type == 'HMAC-SHA256') {
return 32;
} else {
return null;
}
}
function Auth_OpenID_getAllAssociationTypes()
{
return array('HMAC-SHA1', 'HMAC-SHA256');
}
function Auth_OpenID_getSupportedAssociationTypes()
{
$a = array('HMAC-SHA1');
if (Auth_OpenID_HMACSHA256_SUPPORTED) {
$a[] = 'HMAC-SHA256';
}
return $a;
}
function Auth_OpenID_getSessionTypes($assoc_type)
{
$assoc_to_session = array(
'HMAC-SHA1' => array('DH-SHA1', 'no-encryption'));
if (Auth_OpenID_HMACSHA256_SUPPORTED) {
$assoc_to_session['HMAC-SHA256'] =
array('DH-SHA256', 'no-encryption');
}
return Auth_OpenID::arrayGet($assoc_to_session, $assoc_type, array());
}
function Auth_OpenID_checkSessionType($assoc_type, $session_type)
{
if (!in_array($session_type,
Auth_OpenID_getSessionTypes($assoc_type))) {
return false;
}
return true;
}
function Auth_OpenID_getDefaultAssociationOrder()
{
$order = array();
if (!Auth_OpenID_noMathSupport()) {
$order[] = array('HMAC-SHA1', 'DH-SHA1');
if (Auth_OpenID_HMACSHA256_SUPPORTED) {
$order[] = array('HMAC-SHA256', 'DH-SHA256');
}
}
$order[] = array('HMAC-SHA1', 'no-encryption');
if (Auth_OpenID_HMACSHA256_SUPPORTED) {
$order[] = array('HMAC-SHA256', 'no-encryption');
}
return $order;
}
function Auth_OpenID_getOnlyEncryptedOrder()
{
$result = array();
foreach (Auth_OpenID_getDefaultAssociationOrder() as $pair) {
list($assoc, $session) = $pair;
if ($session != 'no-encryption') {
if (Auth_OpenID_HMACSHA256_SUPPORTED &&
($assoc == 'HMAC-SHA256')) {
$result[] = $pair;
} else if ($assoc != 'HMAC-SHA256') {
$result[] = $pair;
}
}
}
return $result;
}
function Auth_OpenID_getDefaultNegotiator()
{
return new Auth_OpenID_SessionNegotiator(
Auth_OpenID_getDefaultAssociationOrder());
}
function Auth_OpenID_getEncryptedNegotiator()
{
return new Auth_OpenID_SessionNegotiator(
Auth_OpenID_getOnlyEncryptedOrder());
}
/**
* A session negotiator controls the allowed and preferred association
* types and association session types. Both the {@link
* Auth_OpenID_Consumer} and {@link Auth_OpenID_Server} use
* negotiators when creating associations.
*
* You can create and use negotiators if you:
* - Do not want to do Diffie-Hellman key exchange because you use
* transport-layer encryption (e.g. SSL)
*
* - Want to use only SHA-256 associations
*
* - Do not want to support plain-text associations over a non-secure
* channel
*
* It is up to you to set a policy for what kinds of associations to
* accept. By default, the library will make any kind of association
* that is allowed in the OpenID 2.0 specification.
*
* Use of negotiators in the library
* =================================
*
* When a consumer makes an association request, it calls {@link
* getAllowedType} to get the preferred association type and
* association session type.
*
* The server gets a request for a particular association/session type
* and calls {@link isAllowed} to determine if it should create an
* association. If it is supported, negotiation is complete. If it is
* not, the server calls {@link getAllowedType} to get an allowed
* association type to return to the consumer.
*
* If the consumer gets an error response indicating that the
* requested association/session type is not supported by the server
* that contains an assocation/session type to try, it calls {@link
* isAllowed} to determine if it should try again with the given
* combination of association/session type.
*
* @package OpenID
*/
class Auth_OpenID_SessionNegotiator {
function Auth_OpenID_SessionNegotiator($allowed_types)
{
$this->allowed_types = array();
$this->setAllowedTypes($allowed_types);
}
/**
* Set the allowed association types, checking to make sure each
* combination is valid.
*
* @access private
*/
function setAllowedTypes($allowed_types)
{
foreach ($allowed_types as $pair) {
list($assoc_type, $session_type) = $pair;
if (!Auth_OpenID_checkSessionType($assoc_type, $session_type)) {
return false;
}
}
$this->allowed_types = $allowed_types;
return true;
}
/**
* Add an association type and session type to the allowed types
* list. The assocation/session pairs are tried in the order that
* they are added.
*
* @access private
*/
function addAllowedType($assoc_type, $session_type = null)
{
if ($this->allowed_types === null) {
$this->allowed_types = array();
}
if ($session_type === null) {
$available = Auth_OpenID_getSessionTypes($assoc_type);
if (!$available) {
return false;
}
foreach ($available as $session_type) {
$this->addAllowedType($assoc_type, $session_type);
}
} else {
if (Auth_OpenID_checkSessionType($assoc_type, $session_type)) {
$this->allowed_types[] = array($assoc_type, $session_type);
} else {
return false;
}
}
return true;
}
// Is this combination of association type and session type allowed?
function isAllowed($assoc_type, $session_type)
{
$assoc_good = in_array(array($assoc_type, $session_type),
$this->allowed_types);
$matches = in_array($session_type,
Auth_OpenID_getSessionTypes($assoc_type));
return ($assoc_good && $matches);
}
/**
* Get a pair of assocation type and session type that are
* supported.
*/
function getAllowedType()
{
if (!$this->allowed_types) {
return array(null, null);
}
return $this->allowed_types[0];
}
}
?>

View file

@ -11,8 +11,8 @@
* @access private
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
@ -20,6 +20,11 @@
*/
require_once 'Auth/OpenID/CryptUtil.php';
/**
* Need Auth_OpenID::bytes().
*/
require_once 'Auth/OpenID.php';
/**
* The superclass of all big-integer math implementations
* @access private
@ -145,9 +150,9 @@ class Auth_OpenID_MathLibrary {
list($duplicate, $nbytes) = $duplicate_cache[$rbytes];
} else {
if ($rbytes[0] == "\x00") {
$nbytes = strlen($rbytes) - 1;
$nbytes = Auth_OpenID::bytes($rbytes) - 1;
} else {
$nbytes = strlen($rbytes);
$nbytes = Auth_OpenID::bytes($rbytes);
}
$mxrand = $this->pow(256, $nbytes);
@ -335,15 +340,23 @@ class Auth_OpenID_GmpMathWrapper extends Auth_OpenID_MathLibrary{
* You can define new math library implementations and add them to
* this array.
*/
global $_Auth_OpenID_math_extensions;
$_Auth_OpenID_math_extensions = array(
array('modules' => array('gmp', 'php_gmp'),
'extension' => 'gmp',
'class' => 'Auth_OpenID_GmpMathWrapper'),
array('modules' => array('bcmath', 'php_bcmath'),
'extension' => 'bcmath',
'class' => 'Auth_OpenID_BcMathWrapper')
);
function Auth_OpenID_math_extensions()
{
$result = array();
if (!defined('Auth_OpenID_BUGGY_GMP')) {
$result[] =
array('modules' => array('gmp', 'php_gmp'),
'extension' => 'gmp',
'class' => 'Auth_OpenID_GmpMathWrapper');
}
$result[] = array('modules' => array('bcmath', 'php_bcmath'),
'extension' => 'bcmath',
'class' => 'Auth_OpenID_BcMathWrapper');
return $result;
}
/**
* Detect which (if any) math library is available
@ -353,26 +366,7 @@ function Auth_OpenID_detectMathLibrary($exts)
$loaded = false;
foreach ($exts as $extension) {
// See if the extension specified is already loaded.
if ($extension['extension'] &&
extension_loaded($extension['extension'])) {
$loaded = true;
}
// Try to load dynamic modules.
if (!$loaded) {
foreach ($extension['modules'] as $module) {
if (@dl($module . "." . PHP_SHLIB_SUFFIX)) {
$loaded = true;
break;
}
}
}
// If the load succeeded, supply an instance of
// Auth_OpenID_MathWrapper which wraps the specified
// module's functionality.
if ($loaded) {
if (extension_loaded($extension['extension'])) {
return $extension;
}
}
@ -387,11 +381,11 @@ function Auth_OpenID_detectMathLibrary($exts)
* functionality.
*
* Checks for the existence of an extension module described by the
* local {@link Auth_OpenID_math_extensions} array and returns an
* result of {@link Auth_OpenID_math_extensions()} and returns an
* instance of a wrapper for that extension module. If no extension
* module is found, an instance of {@link Auth_OpenID_MathWrapper} is
* returned, which wraps the native PHP integer implementation. The
* proper calling convention for this method is $lib =&
* proper calling convention for this method is $lib =
* Auth_OpenID_getMathLib().
*
* This function checks for the existence of specific long number
@ -402,7 +396,7 @@ function Auth_OpenID_detectMathLibrary($exts)
*
* @package OpenID
*/
function &Auth_OpenID_getMathLib()
function Auth_OpenID_getMathLib()
{
// The instance of Auth_OpenID_MathWrapper that we choose to
// supply will be stored here, so that subseqent calls to this
@ -413,25 +407,26 @@ function &Auth_OpenID_getMathLib()
return $lib;
}
if (defined('Auth_OpenID_NO_MATH_SUPPORT')) {
if (Auth_OpenID_noMathSupport()) {
$null = null;
return $null;
}
// If this method has not been called before, look at
// $Auth_OpenID_math_extensions and try to find an extension that
// Auth_OpenID_math_extensions and try to find an extension that
// works.
global $_Auth_OpenID_math_extensions;
$ext = Auth_OpenID_detectMathLibrary($_Auth_OpenID_math_extensions);
$ext = Auth_OpenID_detectMathLibrary(Auth_OpenID_math_extensions());
if ($ext === false) {
$tried = array();
foreach ($_Auth_OpenID_math_extensions as $extinfo) {
foreach (Auth_OpenID_math_extensions() as $extinfo) {
$tried[] = $extinfo['extension'];
}
$triedstr = implode(", ", $tried);
define('Auth_OpenID_NO_MATH_SUPPORT', true);
return null;
Auth_OpenID_setNoMathSupport();
$result = null;
return $result;
}
// Instantiate a new wrapper
@ -441,4 +436,16 @@ function &Auth_OpenID_getMathLib()
return $lib;
}
?>
function Auth_OpenID_setNoMathSupport()
{
if (!defined('Auth_OpenID_NO_MATH_SUPPORT')) {
define('Auth_OpenID_NO_MATH_SUPPORT', true);
}
}
function Auth_OpenID_noMathSupport()
{
return defined('Auth_OpenID_NO_MATH_SUPPORT');
}

File diff suppressed because it is too large Load diff

View file

@ -11,8 +11,8 @@
* @access private
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
if (!defined('Auth_OpenID_RAND_SOURCE')) {
@ -37,7 +37,7 @@ class Auth_OpenID_CryptUtil {
* @param int $num_bytes The length of the return value
* @return string $bytes random bytes
*/
function getBytes($num_bytes)
static function getBytes($num_bytes)
{
static $f = null;
$bytes = '';
@ -77,7 +77,7 @@ class Auth_OpenID_CryptUtil {
* @return string $result A string of randomly-chosen characters
* from $chrs
*/
function randomString($length, $population = null)
static function randomString($length, $population = null)
{
if ($population === null) {
return Auth_OpenID_CryptUtil::getBytes($length);
@ -104,6 +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;
}
}
?>

View file

@ -6,8 +6,8 @@
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
@ -128,4 +128,3 @@ class Auth_OpenID_DatabaseConnection {
}
}
?>

View file

@ -10,12 +10,12 @@
* @access private
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
require_once 'Auth/OpenID.php';
require_once 'Auth/OpenID/BigMath.php';
require_once 'Auth/OpenID/HMACSHA1.php';
function Auth_OpenID_getDefaultMod()
{
@ -51,9 +51,9 @@ class Auth_OpenID_DiffieHellman {
$private = null, $lib = null)
{
if ($lib === null) {
$this->lib =& Auth_OpenID_getMathLib();
$this->lib = Auth_OpenID_getMathLib();
} else {
$this->lib =& $lib;
$this->lib = $lib;
}
if ($mod === null) {
@ -89,93 +89,25 @@ class Auth_OpenID_DiffieHellman {
return $this->public;
}
/**
* Generate the arguments for an OpenID Diffie-Hellman association
* request
*/
function getAssocArgs()
{
$cpub = $this->lib->longToBase64($this->getPublicKey());
$args = array(
'openid.dh_consumer_public' => $cpub,
'openid.session_type' => 'DH-SHA1'
);
if ($this->lib->cmp($this->mod, Auth_OpenID_getDefaultMod()) ||
$this->lib->cmp($this->gen, Auth_OpenID_getDefaultGen())) {
$args['openid.dh_modulus'] = $this->lib->longToBase64($this->mod);
$args['openid.dh_gen'] = $this->lib->longToBase64($this->gen);
}
return $args;
}
function usingDefaultValues()
{
return ($this->mod == Auth_OpenID_getDefaultMod() &&
$this->gen == Auth_OpenID_getDefaultGen());
}
/**
* Perform the server side of the OpenID Diffie-Hellman association
*/
function serverAssociate($consumer_args, $assoc_secret)
{
$lib =& Auth_OpenID_getMathLib();
if (isset($consumer_args['openid.dh_modulus'])) {
$mod = $lib->base64ToLong($consumer_args['openid.dh_modulus']);
} else {
$mod = null;
}
if (isset($consumer_args['openid.dh_gen'])) {
$gen = $lib->base64ToLong($consumer_args['openid.dh_gen']);
} else {
$gen = null;
}
$cpub64 = @$consumer_args['openid.dh_consumer_public'];
if (!isset($cpub64)) {
return false;
}
$dh = new Auth_OpenID_DiffieHellman($mod, $gen);
$cpub = $lib->base64ToLong($cpub64);
$mac_key = $dh->xorSecret($cpub, $assoc_secret);
$enc_mac_key = base64_encode($mac_key);
$spub64 = $lib->longToBase64($dh->getPublicKey());
$server_args = array(
'session_type' => 'DH-SHA1',
'dh_server_public' => $spub64,
'enc_mac_key' => $enc_mac_key
);
return $server_args;
}
function consumerFinish($reply)
{
$spub = $this->lib->base64ToLong($reply['dh_server_public']);
if ($this->lib->cmp($spub, 0) <= 0) {
return false;
}
$enc_mac_key = base64_decode($reply['enc_mac_key']);
return $this->xorSecret($spub, $enc_mac_key);
}
function xorSecret($composite, $secret)
function xorSecret($composite, $secret, $hash_func)
{
$dh_shared = $this->getSharedSecret($composite);
$dh_shared_str = $this->lib->longToBinary($dh_shared);
$sha1_dh_shared = Auth_OpenID_SHA1($dh_shared_str);
$hash_dh_shared = $hash_func($dh_shared_str);
$xsecret = "";
for ($i = 0; $i < strlen($secret); $i++) {
$xsecret .= chr(ord($secret[$i]) ^ ord($sha1_dh_shared[$i]));
for ($i = 0; $i < Auth_OpenID::bytes($secret); $i++) {
$xsecret .= chr(ord($secret[$i]) ^ ord($hash_dh_shared[$i]));
}
return $xsecret;
}
}

View file

@ -6,13 +6,57 @@
require_once "Auth/OpenID.php";
require_once "Auth/OpenID/Parse.php";
require_once "Services/Yadis/XRIRes.php";
require_once "Services/Yadis/Yadis.php";
require_once "Auth/OpenID/Message.php";
require_once "Auth/Yadis/XRIRes.php";
require_once "Auth/Yadis/Yadis.php";
define('_OPENID_1_0_NS', 'http://openid.net/xmlns/1.0');
define('_OPENID_1_2_TYPE', 'http://openid.net/signon/1.2');
define('_OPENID_1_1_TYPE', 'http://openid.net/signon/1.1');
define('_OPENID_1_0_TYPE', 'http://openid.net/signon/1.0');
// XML namespace value
define('Auth_OpenID_XMLNS_1_0', 'http://openid.net/xmlns/1.0');
// Yadis service types
define('Auth_OpenID_TYPE_1_2', 'http://openid.net/signon/1.2');
define('Auth_OpenID_TYPE_1_1', 'http://openid.net/signon/1.1');
define('Auth_OpenID_TYPE_1_0', 'http://openid.net/signon/1.0');
define('Auth_OpenID_TYPE_2_0_IDP', 'http://specs.openid.net/auth/2.0/server');
define('Auth_OpenID_TYPE_2_0', 'http://specs.openid.net/auth/2.0/signon');
define('Auth_OpenID_RP_RETURN_TO_URL_TYPE',
'http://specs.openid.net/auth/2.0/return_to');
function Auth_OpenID_getOpenIDTypeURIs()
{
return array(Auth_OpenID_TYPE_2_0_IDP,
Auth_OpenID_TYPE_2_0,
Auth_OpenID_TYPE_1_2,
Auth_OpenID_TYPE_1_1,
Auth_OpenID_TYPE_1_0);
}
function Auth_OpenID_getOpenIDConsumerTypeURIs()
{
return array(Auth_OpenID_RP_RETURN_TO_URL_TYPE);
}
/*
* Provides a user-readable interpretation of a type uri.
* Useful for error messages.
*/
function Auth_OpenID_getOpenIDTypeName($type_uri) {
switch ($type_uri) {
case Auth_OpenID_TYPE_2_0_IDP:
return 'OpenID 2.0 IDP';
case Auth_OpenID_TYPE_2_0:
return 'OpenID 2.0';
case Auth_OpenID_TYPE_1_2:
return 'OpenID 1.2';
case Auth_OpenID_TYPE_1_1:
return 'OpenID 1.1';
case Auth_OpenID_TYPE_1_0:
return 'OpenID 1.0';
case Auth_OpenID_RP_RETURN_TO_URL_TYPE:
return 'OpenID relying party';
}
}
/**
* Object representing an OpenID service endpoint.
@ -20,12 +64,34 @@ define('_OPENID_1_0_TYPE', 'http://openid.net/signon/1.0');
class Auth_OpenID_ServiceEndpoint {
function Auth_OpenID_ServiceEndpoint()
{
$this->identity_url = null;
$this->claimed_id = null;
$this->server_url = null;
$this->type_uris = array();
$this->delegate = null;
$this->local_id = null;
$this->canonicalID = null;
$this->used_yadis = false; // whether this came from an XRDS
$this->display_identifier = null;
}
function getDisplayIdentifier()
{
if ($this->display_identifier) {
return $this->display_identifier;
}
if (! $this->claimed_id) {
return $this->claimed_id;
}
$parsed = parse_url($this->claimed_id);
$scheme = $parsed['scheme'];
$host = $parsed['host'];
$path = $parsed['path'];
if (array_key_exists('query', $parsed)) {
$query = $parsed['query'];
$no_frag = "$scheme://$host$path?$query";
} else {
$no_frag = "$scheme://$host$path";
}
return $no_frag;
}
function usesExtension($extension_uri)
@ -33,82 +99,263 @@ class Auth_OpenID_ServiceEndpoint {
return in_array($extension_uri, $this->type_uris);
}
function preferredNamespace()
{
if (in_array(Auth_OpenID_TYPE_2_0_IDP, $this->type_uris) ||
in_array(Auth_OpenID_TYPE_2_0, $this->type_uris)) {
return Auth_OpenID_OPENID2_NS;
} else {
return Auth_OpenID_OPENID1_NS;
}
}
/*
* Query this endpoint to see if it has any of the given type
* URIs. This is useful for implementing other endpoint classes
* that e.g. need to check for the presence of multiple versions
* of a single protocol.
*
* @param $type_uris The URIs that you wish to check
*
* @return all types that are in both in type_uris and
* $this->type_uris
*/
function matchTypes($type_uris)
{
$result = array();
foreach ($type_uris as $test_uri) {
if ($this->supportsType($test_uri)) {
$result[] = $test_uri;
}
}
return $result;
}
function supportsType($type_uri)
{
// Does this endpoint support this type?
return ((in_array($type_uri, $this->type_uris)) ||
(($type_uri == Auth_OpenID_TYPE_2_0) &&
$this->isOPIdentifier()));
}
function compatibilityMode()
{
return $this->preferredNamespace() != Auth_OpenID_OPENID2_NS;
}
function isOPIdentifier()
{
return in_array(Auth_OpenID_TYPE_2_0_IDP, $this->type_uris);
}
static function fromOPEndpointURL($op_endpoint_url)
{
// Construct an OP-Identifier OpenIDServiceEndpoint object for
// a given OP Endpoint URL
$obj = new Auth_OpenID_ServiceEndpoint();
$obj->server_url = $op_endpoint_url;
$obj->type_uris = array(Auth_OpenID_TYPE_2_0_IDP);
return $obj;
}
function parseService($yadis_url, $uri, $type_uris, $service_element)
{
// Set the state of this object based on the contents of the
// service element.
// service element. Return true if successful, false if not
// (if findOPLocalIdentifier returns false).
$this->type_uris = $type_uris;
$this->identity_url = $yadis_url;
$this->server_url = $uri;
$this->delegate = Auth_OpenID_ServiceEndpoint::findDelegate(
$service_element);
$this->used_yadis = true;
}
function findDelegate($service)
{
// Extract a openid:Delegate value from a Yadis Service
// element. If no delegate is found, returns null.
// Try to register new namespace.
$service->parser->registerNamespace('openid',
'http://openid.net/xmlns/1.0');
// XXX: should this die if there is more than one delegate
// element?
$delegates = $service->getElements("openid:Delegate");
if ($delegates) {
return $service->parser->content($delegates[0]);
} else {
return null;
if (!$this->isOPIdentifier()) {
$this->claimed_id = $yadis_url;
$this->local_id = Auth_OpenID_findOPLocalIdentifier(
$service_element,
$this->type_uris);
if ($this->local_id === false) {
return false;
}
}
return true;
}
function getServerID()
function getLocalID()
{
// Return the identifier that should be sent as the
// openid.identity_url parameter to the server.
if ($this->delegate === null) {
if ($this->canonicalID) {
return $this->canonicalID;
} else {
return $this->identity_url;
}
if ($this->local_id === null && $this->canonicalID === null) {
return $this->claimed_id;
} else {
return $this->delegate;
if ($this->local_id) {
return $this->local_id;
} else {
return $this->canonicalID;
}
}
}
function fromHTML($uri, $html)
/*
* Parse the given document as XRDS looking for OpenID consumer services.
*
* @return array of Auth_OpenID_ServiceEndpoint or null if the
* document cannot be parsed.
*/
function consumerFromXRDS($uri, $xrds_text)
{
// Parse the given document as HTML looking for an OpenID <link
// rel=...>
$urls = Auth_OpenID_legacy_discover($html);
if ($urls === false) {
return null;
$xrds =& Auth_Yadis_XRDS::parseXRDS($xrds_text);
if ($xrds) {
$yadis_services =
$xrds->services(array('filter_MatchesAnyOpenIDConsumerType'));
return Auth_OpenID_makeOpenIDEndpoints($uri, $yadis_services);
}
list($delegate_url, $server_url) = $urls;
return null;
}
$service = new Auth_OpenID_ServiceEndpoint();
$service->identity_url = $uri;
$service->delegate = $delegate_url;
$service->server_url = $server_url;
$service->type_uris = array(_OPENID_1_0_TYPE);
return $service;
/*
* Parse the given document as XRDS looking for OpenID services.
*
* @return array of Auth_OpenID_ServiceEndpoint or null if the
* document cannot be parsed.
*/
static function fromXRDS($uri, $xrds_text)
{
$xrds = Auth_Yadis_XRDS::parseXRDS($xrds_text);
if ($xrds) {
$yadis_services =
$xrds->services(array('filter_MatchesAnyOpenIDType'));
return Auth_OpenID_makeOpenIDEndpoints($uri, $yadis_services);
}
return null;
}
/*
* Create endpoints from a DiscoveryResult.
*
* @param discoveryResult Auth_Yadis_DiscoveryResult
* @return array of Auth_OpenID_ServiceEndpoint or null if
* endpoints cannot be created.
*/
static function fromDiscoveryResult($discoveryResult)
{
if ($discoveryResult->isXRDS()) {
return Auth_OpenID_ServiceEndpoint::fromXRDS(
$discoveryResult->normalized_uri,
$discoveryResult->response_text);
} else {
return Auth_OpenID_ServiceEndpoint::fromHTML(
$discoveryResult->normalized_uri,
$discoveryResult->response_text);
}
}
static function fromHTML($uri, $html)
{
$discovery_types = array(
array(Auth_OpenID_TYPE_2_0,
'openid2.provider', 'openid2.local_id'),
array(Auth_OpenID_TYPE_1_1,
'openid.server', 'openid.delegate')
);
$services = array();
foreach ($discovery_types as $triple) {
list($type_uri, $server_rel, $delegate_rel) = $triple;
$urls = Auth_OpenID_legacy_discover($html, $server_rel,
$delegate_rel);
if ($urls === false) {
continue;
}
list($delegate_url, $server_url) = $urls;
$service = new Auth_OpenID_ServiceEndpoint();
$service->claimed_id = $uri;
$service->local_id = $delegate_url;
$service->server_url = $server_url;
$service->type_uris = array($type_uri);
$services[] = $service;
}
return $services;
}
function copy()
{
$x = new Auth_OpenID_ServiceEndpoint();
$x->claimed_id = $this->claimed_id;
$x->server_url = $this->server_url;
$x->type_uris = $this->type_uris;
$x->local_id = $this->local_id;
$x->canonicalID = $this->canonicalID;
$x->used_yadis = $this->used_yadis;
return $x;
}
}
function filter_MatchesAnyOpenIDType(&$service)
function Auth_OpenID_findOPLocalIdentifier($service, $type_uris)
{
// Extract a openid:Delegate value from a Yadis Service element.
// If no delegate is found, returns null. Returns false on
// discovery failure (when multiple delegate/localID tags have
// different values).
$service->parser->registerNamespace('openid',
Auth_OpenID_XMLNS_1_0);
$service->parser->registerNamespace('xrd',
Auth_Yadis_XMLNS_XRD_2_0);
$parser = $service->parser;
$permitted_tags = array();
if (in_array(Auth_OpenID_TYPE_1_1, $type_uris) ||
in_array(Auth_OpenID_TYPE_1_0, $type_uris)) {
$permitted_tags[] = 'openid:Delegate';
}
if (in_array(Auth_OpenID_TYPE_2_0, $type_uris)) {
$permitted_tags[] = 'xrd:LocalID';
}
$local_id = null;
foreach ($permitted_tags as $tag_name) {
$tags = $service->getElements($tag_name);
foreach ($tags as $tag) {
$content = $parser->content($tag);
if ($local_id === null) {
$local_id = $content;
} else if ($local_id != $content) {
return false;
}
}
}
return $local_id;
}
function filter_MatchesAnyOpenIDType($service)
{
$uris = $service->getTypes();
foreach ($uris as $uri) {
if (in_array($uri,
array(_OPENID_1_0_TYPE,
_OPENID_1_1_TYPE,
_OPENID_1_2_TYPE))) {
if (in_array($uri, Auth_OpenID_getOpenIDTypeURIs())) {
return true;
}
}
@ -116,15 +363,94 @@ function filter_MatchesAnyOpenIDType(&$service)
return false;
}
function Auth_OpenID_makeOpenIDEndpoints($uri, $endpoints)
function filter_MatchesAnyOpenIDConsumerType(&$service)
{
$uris = $service->getTypes();
foreach ($uris as $uri) {
if (in_array($uri, Auth_OpenID_getOpenIDConsumerTypeURIs())) {
return true;
}
}
return false;
}
function Auth_OpenID_bestMatchingService($service, $preferred_types)
{
// Return the index of the first matching type, or something
// higher if no type matches.
//
// This provides an ordering in which service elements that
// contain a type that comes earlier in the preferred types list
// come before service elements that come later. If a service
// element has more than one type, the most preferred one wins.
foreach ($preferred_types as $index => $typ) {
if (in_array($typ, $service->type_uris)) {
return $index;
}
}
return count($preferred_types);
}
function Auth_OpenID_arrangeByType($service_list, $preferred_types)
{
// Rearrange service_list in a new list so services are ordered by
// types listed in preferred_types. Return the new list.
// Build a list with the service elements in tuples whose
// comparison will prefer the one with the best matching service
$prio_services = array();
foreach ($service_list as $index => $service) {
$prio_services[] = array(Auth_OpenID_bestMatchingService($service,
$preferred_types),
$index, $service);
}
sort($prio_services);
// Now that the services are sorted by priority, remove the sort
// keys from the list.
foreach ($prio_services as $index => $s) {
$prio_services[$index] = $prio_services[$index][2];
}
return $prio_services;
}
// Extract OP Identifier services. If none found, return the rest,
// sorted with most preferred first according to
// OpenIDServiceEndpoint.openid_type_uris.
//
// openid_services is a list of OpenIDServiceEndpoint objects.
//
// Returns a list of OpenIDServiceEndpoint objects."""
function Auth_OpenID_getOPOrUserServices($openid_services)
{
$op_services = Auth_OpenID_arrangeByType($openid_services,
array(Auth_OpenID_TYPE_2_0_IDP));
$openid_services = Auth_OpenID_arrangeByType($openid_services,
Auth_OpenID_getOpenIDTypeURIs());
if ($op_services) {
return $op_services;
} else {
return $openid_services;
}
}
function Auth_OpenID_makeOpenIDEndpoints($uri, $yadis_services)
{
$s = array();
if (!$endpoints) {
if (!$yadis_services) {
return $s;
}
foreach ($endpoints as $service) {
foreach ($yadis_services as $service) {
$type_uris = $service->getTypes();
$uris = $service->getURIs();
@ -132,15 +458,14 @@ function Auth_OpenID_makeOpenIDEndpoints($uri, $endpoints)
// specified, then this is an OpenID endpoint
if ($type_uris &&
$uris) {
foreach ($uris as $service_uri) {
$openid_endpoint = new Auth_OpenID_ServiceEndpoint();
$openid_endpoint->parseService($uri,
$service_uri,
$type_uris,
$service);
$s[] = $openid_endpoint;
if ($openid_endpoint->parseService($uri,
$service_uri,
$type_uris,
$service)) {
$s[] = $openid_endpoint;
}
}
}
}
@ -148,7 +473,9 @@ function Auth_OpenID_makeOpenIDEndpoints($uri, $endpoints)
return $s;
}
function Auth_OpenID_discoverWithYadis($uri, &$fetcher)
function Auth_OpenID_discoverWithYadis($uri, $fetcher,
$endpoint_filter='Auth_OpenID_getOPOrUserServices',
$discover_function=null)
{
// Discover OpenID services for a URI. Tries Yadis and falls back
// on old-style <link rel='...'> discovery if Yadis fails.
@ -157,102 +484,123 @@ function Auth_OpenID_discoverWithYadis($uri, &$fetcher)
// came back for that URI at all. I don't think falling back to
// OpenID 1.0 discovery on the same URL will help, so don't bother
// to catch it.
if ($discover_function === null) {
$discover_function = array('Auth_Yadis_Yadis', 'discover');
}
$openid_services = array();
$http_response = null;
$response = Services_Yadis_Yadis::discover($uri, $http_response,
$fetcher);
$response = call_user_func_array($discover_function,
array($uri, $fetcher));
if ($response) {
$identity_url = $response->uri;
$openid_services =
$response->xrds->services(array('filter_MatchesAnyOpenIDType'));
$yadis_url = $response->normalized_uri;
$yadis_services = array();
if ($response->isFailure() && !$response->isXRDS()) {
return array($uri, array());
}
if (!$openid_services) {
return @Auth_OpenID_discoverWithoutYadis($uri,
$fetcher);
}
$openid_services = Auth_OpenID_ServiceEndpoint::fromXRDS(
$yadis_url,
$response->response_text);
if (!$openid_services) {
$body = $response->body;
if ($response->isXRDS()) {
return Auth_OpenID_discoverWithoutYadis($uri,
$fetcher);
}
// Try to parse the response as HTML to get OpenID 1.0/1.1
// <link rel="...">
$service = Auth_OpenID_ServiceEndpoint::fromHTML($identity_url,
$body);
if ($service !== null) {
$openid_services = array($service);
}
} else {
$openid_services = Auth_OpenID_makeOpenIDEndpoints($response->uri,
$openid_services);
$openid_services = Auth_OpenID_ServiceEndpoint::fromHTML(
$yadis_url,
$response->response_text);
}
return array($identity_url, $openid_services, $http_response);
$openid_services = call_user_func_array($endpoint_filter,
array($openid_services));
return array($yadis_url, $openid_services);
}
function _Auth_OpenID_discoverServiceList($uri, &$fetcher)
function Auth_OpenID_discoverURI($uri, $fetcher)
{
list($url, $services, $resp) = Auth_OpenID_discoverWithYadis($uri,
$fetcher);
return $services;
$uri = Auth_OpenID::normalizeUrl($uri);
return Auth_OpenID_discoverWithYadis($uri, $fetcher);
}
function _Auth_OpenID_discoverXRIServiceList($uri, &$fetcher)
{
list($url, $services, $resp) = _Auth_OpenID_discoverXRI($uri,
$fetcher);
return $services;
}
function Auth_OpenID_discoverWithoutYadis($uri, &$fetcher)
function Auth_OpenID_discoverWithoutYadis($uri, $fetcher)
{
$http_resp = @$fetcher->get($uri);
if ($http_resp->status != 200) {
return array(null, array(), $http_resp);
if ($http_resp->status != 200 and $http_resp->status != 206) {
return array($uri, array());
}
$identity_url = $http_resp->final_url;
// Try to parse the response as HTML to get OpenID 1.0/1.1 <link
// rel="...">
$endpoint = new Auth_OpenID_ServiceEndpoint();
$service = $endpoint->fromHTML($identity_url, $http_resp->body);
if ($service === null) {
$openid_services = array();
} else {
$openid_services = array($service);
}
$openid_services = Auth_OpenID_ServiceEndpoint::fromHTML(
$identity_url,
$http_resp->body);
return array($identity_url, $openid_services, $http_resp);
return array($identity_url, $openid_services);
}
function _Auth_OpenID_discoverXRI($iname, &$fetcher)
function Auth_OpenID_discoverXRI($iname, $fetcher)
{
$services = new Services_Yadis_ProxyResolver($fetcher);
list($canonicalID, $service_list) = $services->query($iname,
array(_OPENID_1_0_TYPE,
_OPENID_1_1_TYPE,
_OPENID_1_2_TYPE),
array('filter_MatchesAnyOpenIDType'));
$resolver = new Auth_Yadis_ProxyResolver($fetcher);
list($canonicalID, $yadis_services) =
$resolver->query($iname,
Auth_OpenID_getOpenIDTypeURIs(),
array('filter_MatchesAnyOpenIDType'));
$endpoints = Auth_OpenID_makeOpenIDEndpoints($iname, $service_list);
$openid_services = Auth_OpenID_makeOpenIDEndpoints($iname,
$yadis_services);
for ($i = 0; $i < count($endpoints); $i++) {
$endpoints[$i]->canonicalID = $canonicalID;
$openid_services = Auth_OpenID_getOPOrUserServices($openid_services);
for ($i = 0; $i < count($openid_services); $i++) {
$openid_services[$i]->canonicalID = $canonicalID;
$openid_services[$i]->claimed_id = $canonicalID;
$openid_services[$i]->display_identifier = $iname;
}
// FIXME: returned xri should probably be in some normal form
return array($iname, $endpoints, null);
return array($iname, $openid_services);
}
function Auth_OpenID_discover($uri, &$fetcher)
function Auth_OpenID_discover($uri, $fetcher)
{
return @Auth_OpenID_discoverWithYadis($uri, $fetcher);
// If the fetcher (i.e., PHP) doesn't support SSL, we can't do
// discovery on an HTTPS URL.
if ($fetcher->isHTTPS($uri) && !$fetcher->supportsSSL()) {
return array($uri, array());
}
if (Auth_Yadis_identifierScheme($uri) == 'XRI') {
$result = Auth_OpenID_discoverXRI($uri, $fetcher);
} else {
$result = Auth_OpenID_discoverURI($uri, $fetcher);
}
// If the fetcher doesn't support SSL, we can't interact with
// HTTPS server URLs; remove those endpoints from the list.
if (!$fetcher->supportsSSL()) {
$http_endpoints = array();
list($new_uri, $endpoints) = $result;
foreach ($endpoints as $e) {
if (!$fetcher->isHTTPS($e->server_url)) {
$http_endpoints[] = $e;
}
}
$result = array($new_uri, $http_endpoints);
}
return $result;
}
?>

View file

@ -10,15 +10,15 @@
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* Import the interface for creating a new store class.
*/
require_once 'Auth/OpenID/Interface.php';
require_once 'Auth/OpenID/HMACSHA1.php';
require_once 'Auth/OpenID/HMAC.php';
/**
* This is a store for use in the worst case, when you have no way of
@ -78,19 +78,12 @@ class Auth_OpenID_DumbStore extends Auth_OpenID_OpenIDStore {
return false;
}
/**
* This implementation does nothing.
*/
function storeNonce($nonce)
{
}
/**
* In a system truly limited to dumb mode, nonces must all be
* accepted. This therefore always returns true, which makes
* replay attacks feasible.
*/
function useNonce($nonce)
function useNonce($server_url, $timestamp, $salt)
{
return true;
}
@ -102,15 +95,5 @@ class Auth_OpenID_DumbStore extends Auth_OpenID_OpenIDStore {
{
return $this->auth_key;
}
/**
* This store is a dumb mode store, so this method is overridden
* to return true.
*/
function isDumb()
{
return true;
}
}
?>

View file

@ -0,0 +1,61 @@
<?php
/**
* An interface for OpenID extensions.
*
* @package OpenID
*/
/**
* Require the Message implementation.
*/
require_once 'Auth/OpenID/Message.php';
/**
* A base class for accessing extension request and response data for
* the OpenID 2 protocol.
*
* @package OpenID
*/
class Auth_OpenID_Extension {
/**
* ns_uri: The namespace to which to add the arguments for this
* extension
*/
var $ns_uri = null;
var $ns_alias = null;
/**
* Get the string arguments that should be added to an OpenID
* message for this extension.
*/
function getExtensionArgs()
{
return null;
}
/**
* Add the arguments from this extension to the provided message.
*
* Returns the message with the extension arguments added.
*/
function toMessage($message)
{
$implicit = $message->isOpenID1();
$added = $message->namespaces->addAlias($this->ns_uri,
$this->ns_alias,
$implicit);
if ($added === null) {
if ($message->namespaces->getAlias($this->ns_uri) !=
$this->ns_alias) {
return null;
}
}
$message->updateArgs($this->ns_uri,
$this->getExtensionArgs());
return $message;
}
}

View file

@ -10,9 +10,8 @@
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
@ -20,7 +19,8 @@
*/
require_once 'Auth/OpenID.php';
require_once 'Auth/OpenID/Interface.php';
require_once 'Auth/OpenID/HMACSHA1.php';
require_once 'Auth/OpenID/HMAC.php';
require_once 'Auth/OpenID/Nonce.php';
/**
* This is a filesystem-based store for OpenID associations and
@ -63,11 +63,9 @@ class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore {
'associations';
// Temp dir must be on the same filesystem as the assciations
// $directory and the $directory containing the auth key file.
// $directory.
$this->temp_dir = $directory . DIRECTORY_SEPARATOR . 'temp';
$this->auth_key_name = $directory . DIRECTORY_SEPARATOR . 'auth_key';
$this->max_nonce_age = 6 * 60 * 60; // Six hours, in seconds
if (!$this->_setup()) {
@ -90,15 +88,14 @@ class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore {
*/
function _setup()
{
return (Auth_OpenID::ensureDir(dirname($this->auth_key_name)) &&
Auth_OpenID::ensureDir($this->nonce_dir) &&
return (Auth_OpenID::ensureDir($this->nonce_dir) &&
Auth_OpenID::ensureDir($this->association_dir) &&
Auth_OpenID::ensureDir($this->temp_dir));
}
/**
* Create a temporary file on the same filesystem as
* $this->auth_key_name and $this->association_dir.
* $this->association_dir.
*
* The temporary directory should not be cleaned if there are any
* processes using the store. If there is no active process using
@ -119,97 +116,26 @@ class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore {
}
}
/**
* Read the auth key from the auth key file. Will return None if
* there is currently no key.
*
* @return mixed
*/
function readAuthKey()
function cleanupNonces()
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
global $Auth_OpenID_SKEW;
$auth_key_file = @fopen($this->auth_key_name, 'rb');
if ($auth_key_file === false) {
return null;
}
$nonces = Auth_OpenID_FileStore::_listdir($this->nonce_dir);
$now = time();
$key = fread($auth_key_file, filesize($this->auth_key_name));
fclose($auth_key_file);
return $key;
}
/**
* Generate a new random auth key and safely store it in the
* location specified by $this->auth_key_name.
*
* @return string $key
*/
function createAuthKey()
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
$auth_key = Auth_OpenID_CryptUtil::randomString($this->AUTH_KEY_LEN);
list($file_obj, $tmp) = $this->_mktemp();
fwrite($file_obj, $auth_key);
fflush($file_obj);
fclose($file_obj);
if (function_exists('link')) {
// Posix filesystem
$saved = link($tmp, $this->auth_key_name);
Auth_OpenID_FileStore::_removeIfPresent($tmp);
} else {
// Windows filesystem
$saved = rename($tmp, $this->auth_key_name);
}
if (!$saved) {
// The link failed, either because we lack the permission,
// or because the file already exists; try to read the key
// in case the file already existed.
$auth_key = $this->readAuthKey();
}
return $auth_key;
}
/**
* Retrieve the auth key from the file specified by
* $this->auth_key_name, creating it if it does not exist.
*
* @return string $key
*/
function getAuthKey()
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
$auth_key = $this->readAuthKey();
if ($auth_key === null) {
$auth_key = $this->createAuthKey();
if (strlen($auth_key) != $this->AUTH_KEY_LEN) {
$fmt = 'Got an invalid auth key from %s. Expected '.
'%d-byte string. Got: %s';
$msg = sprintf($fmt, $this->auth_key_name, $this->AUTH_KEY_LEN,
$auth_key);
trigger_error($msg, E_USER_WARNING);
return null;
$removed = 0;
// Check all nonces for expiry
foreach ($nonces as $nonce_fname) {
$base = basename($nonce_fname);
$parts = explode('-', $base, 2);
$timestamp = $parts[0];
$timestamp = intval($timestamp, 16);
if (abs($timestamp - $now) > $Auth_OpenID_SKEW) {
Auth_OpenID_FileStore::_removeIfPresent($nonce_fname);
$removed += 1;
}
}
return $auth_key;
return $removed;
}
/**
@ -328,16 +254,15 @@ class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore {
// strip off the path to do the comparison
$name = basename($filename);
foreach ($association_files as $association_file) {
if (strpos($association_file, $name) === 0) {
$base = basename($association_file);
if (strpos($base, $name) === 0) {
$matching_files[] = $association_file;
}
}
$matching_associations = array();
// read the matching files and sort by time issued
foreach ($matching_files as $name) {
$full_name = $this->association_dir . DIRECTORY_SEPARATOR .
$name;
foreach ($matching_files as $full_name) {
$association = $this->_getAssociation($full_name);
if ($association !== null) {
$matching_associations[] = array($association->issued,
@ -426,86 +351,60 @@ class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore {
}
}
/**
* Mark this nonce as present.
*/
function storeNonce($nonce)
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
$filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $nonce;
$nonce_file = fopen($filename, 'w');
if ($nonce_file === false) {
return false;
}
fclose($nonce_file);
return true;
}
/**
* Return whether this nonce is present. As a side effect, mark it
* as no longer present.
*
* @return bool $present
*/
function useNonce($nonce)
function useNonce($server_url, $timestamp, $salt)
{
global $Auth_OpenID_SKEW;
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
$filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $nonce;
$st = @stat($filename);
if ($st === false) {
if ( abs($timestamp - time()) > $Auth_OpenID_SKEW ) {
return false;
}
// Either it is too old or we are using it. Either way, we
// must remove the file.
if (!unlink($filename)) {
return false;
if ($server_url) {
list($proto, $rest) = explode('://', $server_url, 2);
} else {
$proto = '';
$rest = '';
}
$now = time();
$nonce_age = $now - $st[9];
$parts = explode('/', $rest, 2);
$domain = $this->_filenameEscape($parts[0]);
$url_hash = $this->_safe64($server_url);
$salt_hash = $this->_safe64($salt);
// We can us it if the age of the file is less than the
// expiration time.
return $nonce_age <= $this->max_nonce_age;
$filename = sprintf('%08x-%s-%s-%s-%s', $timestamp, $proto,
$domain, $url_hash, $salt_hash);
$filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $filename;
$result = @fopen($filename, 'x');
if ($result === false) {
return false;
} else {
fclose($result);
return true;
}
}
/**
* Remove expired entries from the database. This is potentially
* expensive, so only run when it is acceptable to take time.
*
* @access private
*/
function clean()
function _allAssocs()
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
$nonces = Auth_OpenID_FileStore::_listdir($this->nonce_dir);
$now = time();
// Check all nonces for expiry
foreach ($nonces as $nonce) {
$filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $nonce;
$st = @stat($filename);
if ($st !== false) {
// Remove the nonce if it has expired
$nonce_age = $now - $st[9];
if ($nonce_age > $this->max_nonce_age) {
Auth_OpenID_FileStore::_removeIfPresent($filename);
}
}
}
$all_associations = array();
$association_filenames =
Auth_OpenID_FileStore::_listdir($this->association_dir);
@ -528,12 +427,40 @@ class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore {
$association_filename);
} else {
if ($association->getExpiresIn() == 0) {
Auth_OpenID_FileStore::_removeIfPresent(
$association_filename);
$all_associations[] = array($association_filename,
$association);
}
}
}
}
return $all_associations;
}
function clean()
{
if (!$this->active) {
trigger_error("FileStore no longer active", E_USER_ERROR);
return null;
}
$nonces = Auth_OpenID_FileStore::_listdir($this->nonce_dir);
$now = time();
// Check all nonces for expiry
foreach ($nonces as $nonce) {
if (!Auth_OpenID_checkTimestamp($nonce, $now)) {
$filename = $this->nonce_dir . DIRECTORY_SEPARATOR . $nonce;
Auth_OpenID_FileStore::_removeIfPresent($filename);
}
}
foreach ($this->_allAssocs() as $pair) {
list($assoc_filename, $assoc) = $pair;
if ($assoc->getExpiresIn() == 0) {
Auth_OpenID_FileStore::_removeIfPresent($assoc_filename);
}
}
}
/**
@ -592,7 +519,7 @@ class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore {
/**
* @access private
*/
function _mkdtemp($dir)
static function _mkdtemp($dir)
{
foreach (range(0, 4) as $i) {
$name = $dir . strval(DIRECTORY_SEPARATOR) . strval(getmypid()) .
@ -614,7 +541,9 @@ class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore {
$handle = opendir($dir);
$files = array();
while (false !== ($filename = readdir($handle))) {
$files[] = $filename;
if (!in_array($filename, array('.', '..'))) {
$files[] = $dir . DIRECTORY_SEPARATOR . $filename;
}
}
return $files;
}
@ -647,8 +576,10 @@ class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore {
function _filenameEscape($str)
{
$filename = "";
for ($i = 0; $i < strlen($str); $i++) {
$c = $str[$i];
$b = Auth_OpenID::toBytes($str);
for ($i = 0; $i < count($b); $i++) {
$c = $b[$i];
if (Auth_OpenID_FileStore::_isFilenameSafe($c)) {
$filename .= $c;
} else {
@ -669,6 +600,19 @@ class Auth_OpenID_FileStore extends Auth_OpenID_OpenIDStore {
{
return @unlink($filename);
}
function cleanupAssociations()
{
$removed = 0;
foreach ($this->_allAssocs() as $pair) {
list($assoc_filename, $assoc) = $pair;
if ($assoc->getExpiresIn() == 0) {
$this->_removeIfPresent($assoc_filename);
$removed += 1;
}
}
return $removed;
}
}
?>

View file

@ -0,0 +1,105 @@
<?php
/**
* This is the HMACSHA1 implementation for the OpenID library.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @access private
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
require_once 'Auth/OpenID.php';
/**
* SHA1_BLOCKSIZE is this module's SHA1 blocksize used by the fallback
* implementation.
*/
define('Auth_OpenID_SHA1_BLOCKSIZE', 64);
function Auth_OpenID_SHA1($text)
{
if (function_exists('hash') &&
function_exists('hash_algos') &&
(in_array('sha1', hash_algos()))) {
// PHP 5 case (sometimes): 'hash' available and 'sha1' algo
// supported.
return hash('sha1', $text, true);
} else if (function_exists('sha1')) {
// PHP 4 case: 'sha1' available.
$hex = sha1($text);
$raw = '';
for ($i = 0; $i < 40; $i += 2) {
$hexcode = substr($hex, $i, 2);
$charcode = (int)base_convert($hexcode, 16, 10);
$raw .= chr($charcode);
}
return $raw;
} else {
// Explode.
trigger_error('No SHA1 function found', E_USER_ERROR);
}
}
/**
* Compute an HMAC/SHA1 hash.
*
* @access private
* @param string $key The HMAC key
* @param string $text The message text to hash
* @return string $mac The MAC
*/
function Auth_OpenID_HMACSHA1($key, $text)
{
if (Auth_OpenID::bytes($key) > Auth_OpenID_SHA1_BLOCKSIZE) {
$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);
$hash1 = Auth_OpenID_SHA1(($key ^ $ipad) . $text, true);
$hmac = Auth_OpenID_SHA1(($key ^ $opad) . $hash1, true);
return $hmac;
}
if (function_exists('hash') &&
function_exists('hash_algos') &&
(in_array('sha256', hash_algos()))) {
function Auth_OpenID_SHA256($text)
{
// PHP 5 case: 'hash' available and 'sha256' algo supported.
return hash('sha256', $text, true);
}
define('Auth_OpenID_SHA256_SUPPORTED', true);
} else {
define('Auth_OpenID_SHA256_SUPPORTED', false);
}
if (function_exists('hash_hmac') &&
function_exists('hash_algos') &&
(in_array('sha256', hash_algos()))) {
function Auth_OpenID_HMACSHA256($key, $text)
{
// Return raw MAC (not hex string).
return hash_hmac('sha256', $text, $key, true);
}
define('Auth_OpenID_HMACSHA256_SUPPORTED', true);
} else {
define('Auth_OpenID_HMACSHA256_SUPPORTED', false);
}

View file

@ -1,72 +0,0 @@
<?php
/**
* This is the HMACSHA1 implementation for the OpenID library.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @access private
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
/**
* SHA1_BLOCKSIZE is this module's SHA1 blocksize used by the fallback
* implementation.
*/
define('Auth_OpenID_SHA1_BLOCKSIZE', 64);
if (!function_exists('sha1')) {
/**
* Return a raw SHA1 hash of the given string
*
* XXX: include the SHA1 code from Dan Libby's OpenID library
*/
function Auth_OpenID_SHA1($text)
{
trigger_error('No SHA1 function found', E_USER_ERROR);
}
} else {
/**
* @ignore
*/
function Auth_OpenID_SHA1($text)
{
$hex = sha1($text);
$raw = '';
for ($i = 0; $i < 40; $i += 2) {
$hexcode = substr($hex, $i, 2);
$charcode = (int)base_convert($hexcode, 16, 10);
$raw .= chr($charcode);
}
return $raw;
}
}
/**
* Compute an HMAC/SHA1 hash.
*
* @access private
* @param string $key The HMAC key
* @param string $text The message text to hash
* @return string $mac The MAC
*/
function Auth_OpenID_HMACSHA1($key, $text)
{
if (strlen($key) > Auth_OpenID_SHA1_BLOCKSIZE) {
$key = Auth_OpenID_SHA1($key, true);
}
$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);
$hash1 = Auth_OpenID_SHA1(($key ^ $ipad) . $text, true);
$hmac = Auth_OpenID_SHA1(($key ^ $opad) . $hash1, true);
return $hmac;
}
?>

View file

@ -9,8 +9,8 @@
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
@ -20,16 +20,14 @@
* consumers. If you want to create an SQL-driven store, please see
* then {@link Auth_OpenID_SQLStore} class.
*
* Change: Version 2.0 removed the storeNonce, getAuthKey, and isDumb
* methods, and changed the behavior of the useNonce method to support
* one-way nonces.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
*/
class Auth_OpenID_OpenIDStore {
/**
* @var integer The length of the auth key that should be returned
* by the getAuthKey method.
*/
var $AUTH_KEY_LEN = 20;
/**
* This method puts an Association object into storage,
* retrievable by server URL and handle.
@ -49,6 +47,60 @@ class Auth_OpenID_OpenIDStore {
"not implemented", E_USER_ERROR);
}
/*
* Remove expired nonces from the store.
*
* Discards any nonce from storage that is old enough that its
* timestamp would not pass useNonce().
*
* This method is not called in the normal operation of the
* library. It provides a way for store admins to keep their
* storage from filling up with expired data.
*
* @return the number of nonces expired
*/
function cleanupNonces()
{
trigger_error("Auth_OpenID_OpenIDStore::cleanupNonces ".
"not implemented", E_USER_ERROR);
}
/*
* Remove expired associations from the store.
*
* This method is not called in the normal operation of the
* library. It provides a way for store admins to keep their
* storage from filling up with expired data.
*
* @return the number of associations expired.
*/
function cleanupAssociations()
{
trigger_error("Auth_OpenID_OpenIDStore::cleanupAssociations ".
"not implemented", E_USER_ERROR);
}
/*
* Shortcut for cleanupNonces(), cleanupAssociations().
*
* This method is not called in the normal operation of the
* library. It provides a way for store admins to keep their
* storage from filling up with expired data.
*/
function cleanup()
{
return array($this->cleanupNonces(),
$this->cleanupAssociations());
}
/**
* Report whether this storage supports cleanup
*/
function supportsCleanup()
{
return true;
}
/**
* This method returns an Association object from storage that
* matches the server URL and, if specified, handle. It returns
@ -58,7 +110,7 @@ class Auth_OpenID_OpenIDStore {
* If no handle is specified, the store may return any association
* which matches the server URL. If multiple associations are
* valid, the recommended return value for this method is the one
* that will remain valid for the longest duration.
* most recently issued.
*
* This method is allowed (and encouraged) to garbage collect
* expired associations when found. This method must not return
@ -110,73 +162,30 @@ class Auth_OpenID_OpenIDStore {
}
/**
* Stores a nonce. This is used by the consumer to prevent replay
* attacks.
* Called when using a nonce.
*
* @param string $nonce The nonce to store.
* This method should return C{True} if the nonce has not been
* used before, and store it for a while to make sure nobody
* tries to use the same value again. If the nonce has already
* been used, return C{False}.
*
* @return null
*/
function storeNonce($nonce)
{
trigger_error("Auth_OpenID_OpenIDStore::storeNonce ".
"not implemented", E_USER_ERROR);
}
/**
* This method is called when the library is attempting to use a
* nonce. If the nonce is in the store, this method removes it and
* returns a value which evaluates as true. Otherwise it returns a
* value which evaluates as false.
*
* This method is allowed and encouraged to treat nonces older
* than some period (a very conservative window would be 6 hours,
* for example) as no longer existing, and return False and remove
* them.
* Change: In earlier versions, round-trip nonces were used and a
* nonce was only valid if it had been previously stored with
* storeNonce. Version 2.0 uses one-way nonces, requiring a
* different implementation here that does not depend on a
* storeNonce call. (storeNonce is no longer part of the
* interface.
*
* @param string $nonce The nonce to use.
*
* @return bool Whether or not the nonce was valid.
*/
function useNonce($nonce)
function useNonce($server_url, $timestamp, $salt)
{
trigger_error("Auth_OpenID_OpenIDStore::useNonce ".
"not implemented", E_USER_ERROR);
}
/**
* This method returns a key used to sign the tokens, to ensure
* that they haven't been tampered with in transit. It should
* return the same key every time it is called. The key returned
* should be {@link AUTH_KEY_LEN} bytes long.
*
* @return string The key. It should be {@link AUTH_KEY_LEN} bytes in
* length, and use the full range of byte values. That is, it
* should be treated as a lump of binary data stored in a string.
*/
function getAuthKey()
{
trigger_error("Auth_OpenID_OpenIDStore::getAuthKey ".
"not implemented", E_USER_ERROR);
}
/**
* This method must return true if the store is a dumb-mode-style
* store. Unlike all other methods in this class, this one
* provides a default implementation, which returns false.
*
* In general, any custom subclass of {@link Auth_OpenID_OpenIDStore}
* won't override this method, as custom subclasses are only likely to
* be created when the store is fully functional.
*
* @return bool true if the store works fully, false if the
* consumer will have to use dumb mode to use this store.
*/
function isDumb()
{
return false;
}
/**
* Removes all entries from the store; implementation is optional.
*/
@ -185,4 +194,3 @@ class Auth_OpenID_OpenIDStore {
}
}
?>

View file

@ -11,8 +11,8 @@
* @access private
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
@ -26,7 +26,7 @@ class Auth_OpenID_KVForm {
* @static
* @access private
*/
function toArray($kvs, $strict=false)
static function toArray($kvs, $strict=false)
{
$lines = explode("\n", $kvs);
@ -78,7 +78,7 @@ class Auth_OpenID_KVForm {
* @static
* @access private
*/
function fromArray($values)
static function fromArray($values)
{
if ($values === null) {
return null;
@ -109,4 +109,3 @@ class Auth_OpenID_KVForm {
}
}
?>

View 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));
}
}
?>

View file

@ -0,0 +1,207 @@
<?php
/**
* This file supplies a memcached store backend for OpenID servers and
* consumers.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author Artemy Tregubenko <me@arty.name>
* @copyright 2008 JanRain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
* Contributed by Open Web Technologies <http://openwebtech.ru/>
*/
/**
* Import the interface for creating a new store class.
*/
require_once 'Auth/OpenID/Interface.php';
/**
* This is a memcached-based store for OpenID associations and
* nonces.
*
* As memcache has limit of 250 chars for key length,
* server_url, handle and salt are hashed with sha1().
*
* Most of the methods of this class are implementation details.
* People wishing to just use this store need only pay attention to
* the constructor.
*
* @package OpenID
*/
class Auth_OpenID_MemcachedStore extends Auth_OpenID_OpenIDStore {
/**
* Initializes a new {@link Auth_OpenID_MemcachedStore} instance.
* Just saves memcached object as property.
*
* @param resource connection Memcache connection resourse
*/
function Auth_OpenID_MemcachedStore($connection, $compress = false)
{
$this->connection = $connection;
$this->compress = $compress ? MEMCACHE_COMPRESSED : 0;
}
/**
* Store association until its expiration time in memcached.
* Overwrites any existing association with same server_url and
* handle. Handles list of associations for every server.
*/
function storeAssociation($server_url, $association)
{
// create memcached keys for association itself
// and list of associations for this server
$associationKey = $this->associationKey($server_url,
$association->handle);
$serverKey = $this->associationServerKey($server_url);
// get list of associations
$serverAssociations = $this->connection->get($serverKey);
// if no such list, initialize it with empty array
if (!$serverAssociations) {
$serverAssociations = array();
}
// and store given association key in it
$serverAssociations[$association->issued] = $associationKey;
// save associations' keys list
$this->connection->set(
$serverKey,
$serverAssociations,
$this->compress
);
// save association itself
$this->connection->set(
$associationKey,
$association,
$this->compress,
$association->issued + $association->lifetime);
}
/**
* Read association from memcached. If no handle given
* and multiple associations found, returns latest issued
*/
function getAssociation($server_url, $handle = null)
{
// simple case: handle given
if ($handle !== null) {
// get association, return null if failed
$association = $this->connection->get(
$this->associationKey($server_url, $handle));
return $association ? $association : null;
}
// no handle given, working with list
// create key for list of associations
$serverKey = $this->associationServerKey($server_url);
// get list of associations
$serverAssociations = $this->connection->get($serverKey);
// return null if failed or got empty list
if (!$serverAssociations) {
return null;
}
// get key of most recently issued association
$keys = array_keys($serverAssociations);
sort($keys);
$lastKey = $serverAssociations[array_pop($keys)];
// get association, return null if failed
$association = $this->connection->get($lastKey);
return $association ? $association : null;
}
/**
* Immediately delete association from memcache.
*/
function removeAssociation($server_url, $handle)
{
// create memcached keys for association itself
// and list of associations for this server
$serverKey = $this->associationServerKey($server_url);
$associationKey = $this->associationKey($server_url,
$handle);
// get list of associations
$serverAssociations = $this->connection->get($serverKey);
// return null if failed or got empty list
if (!$serverAssociations) {
return false;
}
// ensure that given association key exists in list
$serverAssociations = array_flip($serverAssociations);
if (!array_key_exists($associationKey, $serverAssociations)) {
return false;
}
// remove given association key from list
unset($serverAssociations[$associationKey]);
$serverAssociations = array_flip($serverAssociations);
// save updated list
$this->connection->set(
$serverKey,
$serverAssociations,
$this->compress
);
// delete association
return $this->connection->delete($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;
}
// returns false when nonce already exists
// otherwise adds nonce
return $this->connection->add(
'openid_nonce_' . sha1($server_url) . '_' . sha1($salt),
1, // any value here
$this->compress,
$Auth_OpenID_SKEW);
}
/**
* Memcache key is prefixed with 'openid_association_' string.
*/
function associationKey($server_url, $handle = null)
{
return 'openid_association_' . sha1($server_url) . '_' . sha1($handle);
}
/**
* Memcache key is prefixed with 'openid_association_' string.
*/
function associationServerKey($server_url)
{
return 'openid_association_server_' . sha1($server_url);
}
/**
* Report that this storage doesn't support cleanup
*/
function supportsCleanup()
{
return false;
}
}

View file

@ -0,0 +1,920 @@
<?php
/**
* Extension argument processing code
*
* @package OpenID
*/
/**
* Import tools needed to deal with messages.
*/
require_once 'Auth/OpenID.php';
require_once 'Auth/OpenID/KVForm.php';
require_once 'Auth/Yadis/XML.php';
require_once 'Auth/OpenID/Consumer.php'; // For Auth_OpenID_FailureResponse
// This doesn't REALLY belong here, but where is better?
define('Auth_OpenID_IDENTIFIER_SELECT',
"http://specs.openid.net/auth/2.0/identifier_select");
// URI for Simple Registration extension, the only commonly deployed
// OpenID 1.x extension, and so a special case
define('Auth_OpenID_SREG_URI', 'http://openid.net/sreg/1.0');
// The OpenID 1.X namespace URI
define('Auth_OpenID_OPENID1_NS', 'http://openid.net/signon/1.0');
define('Auth_OpenID_THE_OTHER_OPENID1_NS', 'http://openid.net/signon/1.1');
function Auth_OpenID_isOpenID1($ns)
{
return ($ns == Auth_OpenID_THE_OTHER_OPENID1_NS) ||
($ns == Auth_OpenID_OPENID1_NS);
}
// The OpenID 2.0 namespace URI
define('Auth_OpenID_OPENID2_NS', 'http://specs.openid.net/auth/2.0');
// The namespace consisting of pairs with keys that are prefixed with
// "openid." but not in another namespace.
define('Auth_OpenID_NULL_NAMESPACE', 'Null namespace');
// The null namespace, when it is an allowed OpenID namespace
define('Auth_OpenID_OPENID_NS', 'OpenID namespace');
// The top-level namespace, excluding all pairs with keys that start
// with "openid."
define('Auth_OpenID_BARE_NS', 'Bare namespace');
// Sentinel for Message implementation to indicate that getArg should
// return null instead of returning a default.
define('Auth_OpenID_NO_DEFAULT', 'NO DEFAULT ALLOWED');
// Limit, in bytes, of identity provider and return_to URLs, including
// response payload. See OpenID 1.1 specification, Appendix D.
define('Auth_OpenID_OPENID1_URL_LIMIT', 2047);
// All OpenID protocol fields. Used to check namespace aliases.
global $Auth_OpenID_OPENID_PROTOCOL_FIELDS;
$Auth_OpenID_OPENID_PROTOCOL_FIELDS = array(
'ns', 'mode', 'error', 'return_to', 'contact', 'reference',
'signed', 'assoc_type', 'session_type', 'dh_modulus', 'dh_gen',
'dh_consumer_public', 'claimed_id', 'identity', 'realm',
'invalidate_handle', 'op_endpoint', 'response_nonce', 'sig',
'assoc_handle', 'trust_root', 'openid');
// Global namespace / alias registration map. See
// Auth_OpenID_registerNamespaceAlias.
global $Auth_OpenID_registered_aliases;
$Auth_OpenID_registered_aliases = array();
/**
* Registers a (namespace URI, alias) mapping in a global namespace
* alias map. Raises NamespaceAliasRegistrationError if either the
* namespace URI or alias has already been registered with a different
* value. This function is required if you want to use a namespace
* with an OpenID 1 message.
*/
function Auth_OpenID_registerNamespaceAlias($namespace_uri, $alias)
{
global $Auth_OpenID_registered_aliases;
if (Auth_OpenID::arrayGet($Auth_OpenID_registered_aliases,
$alias) == $namespace_uri) {
return true;
}
if (in_array($namespace_uri,
array_values($Auth_OpenID_registered_aliases))) {
return false;
}
if (in_array($alias, array_keys($Auth_OpenID_registered_aliases))) {
return false;
}
$Auth_OpenID_registered_aliases[$alias] = $namespace_uri;
return true;
}
/**
* Removes a (namespace_uri, alias) registration from the global
* namespace alias map. Returns true if the removal succeeded; false
* if not (if the mapping did not exist).
*/
function Auth_OpenID_removeNamespaceAlias($namespace_uri, $alias)
{
global $Auth_OpenID_registered_aliases;
if (Auth_OpenID::arrayGet($Auth_OpenID_registered_aliases,
$alias) === $namespace_uri) {
unset($Auth_OpenID_registered_aliases[$alias]);
return true;
}
return false;
}
/**
* An Auth_OpenID_Mapping maintains a mapping from arbitrary keys to
* arbitrary values. (This is unlike an ordinary PHP array, whose
* keys may be only simple scalars.)
*
* @package OpenID
*/
class Auth_OpenID_Mapping {
/**
* Initialize a mapping. If $classic_array is specified, its keys
* and values are used to populate the mapping.
*/
function Auth_OpenID_Mapping($classic_array = null)
{
$this->keys = array();
$this->values = array();
if (is_array($classic_array)) {
foreach ($classic_array as $key => $value) {
$this->set($key, $value);
}
}
}
/**
* Returns true if $thing is an Auth_OpenID_Mapping object; false
* if not.
*/
static function isA($thing)
{
return (is_object($thing) &&
strtolower(get_class($thing)) == 'auth_openid_mapping');
}
/**
* Returns an array of the keys in the mapping.
*/
function keys()
{
return $this->keys;
}
/**
* Returns an array of values in the mapping.
*/
function values()
{
return $this->values;
}
/**
* Returns an array of (key, value) pairs in the mapping.
*/
function items()
{
$temp = array();
for ($i = 0; $i < count($this->keys); $i++) {
$temp[] = array($this->keys[$i],
$this->values[$i]);
}
return $temp;
}
/**
* Returns the "length" of the mapping, or the number of keys.
*/
function len()
{
return count($this->keys);
}
/**
* Sets a key-value pair in the mapping. If the key already
* exists, its value is replaced with the new value.
*/
function set($key, $value)
{
$index = array_search($key, $this->keys);
if ($index !== false) {
$this->values[$index] = $value;
} else {
$this->keys[] = $key;
$this->values[] = $value;
}
}
/**
* Gets a specified value from the mapping, associated with the
* specified key. If the key does not exist in the mapping,
* $default is returned instead.
*/
function get($key, $default = null)
{
$index = array_search($key, $this->keys);
if ($index !== false) {
return $this->values[$index];
} else {
return $default;
}
}
/**
* @access private
*/
function _reflow()
{
// PHP is broken yet again. Sort the arrays to remove the
// hole in the numeric indexes that make up the array.
$old_keys = $this->keys;
$old_values = $this->values;
$this->keys = array();
$this->values = array();
foreach ($old_keys as $k) {
$this->keys[] = $k;
}
foreach ($old_values as $v) {
$this->values[] = $v;
}
}
/**
* Deletes a key-value pair from the mapping with the specified
* key.
*/
function del($key)
{
$index = array_search($key, $this->keys);
if ($index !== false) {
unset($this->keys[$index]);
unset($this->values[$index]);
$this->_reflow();
return true;
}
return false;
}
/**
* Returns true if the specified value has a key in the mapping;
* false if not.
*/
function contains($value)
{
return (array_search($value, $this->keys) !== false);
}
}
/**
* Maintains a bijective map between namespace uris and aliases.
*
* @package OpenID
*/
class Auth_OpenID_NamespaceMap {
function Auth_OpenID_NamespaceMap()
{
$this->alias_to_namespace = new Auth_OpenID_Mapping();
$this->namespace_to_alias = new Auth_OpenID_Mapping();
$this->implicit_namespaces = array();
}
function getAlias($namespace_uri)
{
return $this->namespace_to_alias->get($namespace_uri);
}
function getNamespaceURI($alias)
{
return $this->alias_to_namespace->get($alias);
}
function iterNamespaceURIs()
{
// Return an iterator over the namespace URIs
return $this->namespace_to_alias->keys();
}
function iterAliases()
{
// Return an iterator over the aliases"""
return $this->alias_to_namespace->keys();
}
function iteritems()
{
return $this->namespace_to_alias->items();
}
function isImplicit($namespace_uri)
{
return in_array($namespace_uri, $this->implicit_namespaces);
}
function addAlias($namespace_uri, $desired_alias, $implicit=false)
{
// Add an alias from this namespace URI to the desired alias
global $Auth_OpenID_OPENID_PROTOCOL_FIELDS;
// Check that desired_alias is not an openid protocol field as
// per the spec.
if (in_array($desired_alias, $Auth_OpenID_OPENID_PROTOCOL_FIELDS)) {
Auth_OpenID::log("\"%s\" is not an allowed namespace alias",
$desired_alias);
return null;
}
// Check that desired_alias does not contain a period as per
// the spec.
if (strpos($desired_alias, '.') !== false) {
Auth_OpenID::log('"%s" must not contain a dot', $desired_alias);
return null;
}
// Check that there is not a namespace already defined for the
// desired alias
$current_namespace_uri =
$this->alias_to_namespace->get($desired_alias);
if (($current_namespace_uri !== null) &&
($current_namespace_uri != $namespace_uri)) {
Auth_OpenID::log('Cannot map "%s" because previous mapping exists',
$namespace_uri);
return null;
}
// Check that there is not already a (different) alias for
// this namespace URI
$alias = $this->namespace_to_alias->get($namespace_uri);
if (($alias !== null) && ($alias != $desired_alias)) {
Auth_OpenID::log('Cannot map %s to alias %s. ' .
'It is already mapped to alias %s',
$namespace_uri, $desired_alias, $alias);
return null;
}
assert((Auth_OpenID_NULL_NAMESPACE === $desired_alias) ||
is_string($desired_alias));
$this->alias_to_namespace->set($desired_alias, $namespace_uri);
$this->namespace_to_alias->set($namespace_uri, $desired_alias);
if ($implicit) {
array_push($this->implicit_namespaces, $namespace_uri);
}
return $desired_alias;
}
function add($namespace_uri)
{
// Add this namespace URI to the mapping, without caring what
// alias it ends up with
// See if this namespace is already mapped to an alias
$alias = $this->namespace_to_alias->get($namespace_uri);
if ($alias !== null) {
return $alias;
}
// Fall back to generating a numerical alias
$i = 0;
while (1) {
$alias = 'ext' . strval($i);
if ($this->addAlias($namespace_uri, $alias) === null) {
$i += 1;
} else {
return $alias;
}
}
// Should NEVER be reached!
return null;
}
function contains($namespace_uri)
{
return $this->isDefined($namespace_uri);
}
function isDefined($namespace_uri)
{
return $this->namespace_to_alias->contains($namespace_uri);
}
}
/**
* In the implementation of this object, null represents the global
* namespace as well as a namespace with no key.
*
* @package OpenID
*/
class Auth_OpenID_Message {
function Auth_OpenID_Message($openid_namespace = null)
{
// Create an empty Message
$this->allowed_openid_namespaces = array(
Auth_OpenID_OPENID1_NS,
Auth_OpenID_THE_OTHER_OPENID1_NS,
Auth_OpenID_OPENID2_NS);
$this->args = new Auth_OpenID_Mapping();
$this->namespaces = new Auth_OpenID_NamespaceMap();
if ($openid_namespace === null) {
$this->_openid_ns_uri = null;
} else {
$implicit = Auth_OpenID_isOpenID1($openid_namespace);
$this->setOpenIDNamespace($openid_namespace, $implicit);
}
}
function isOpenID1()
{
return Auth_OpenID_isOpenID1($this->getOpenIDNamespace());
}
function isOpenID2()
{
return $this->getOpenIDNamespace() == Auth_OpenID_OPENID2_NS;
}
static function fromPostArgs($args)
{
// Construct a Message containing a set of POST arguments
$obj = new Auth_OpenID_Message();
// Partition into "openid." args and bare args
$openid_args = array();
foreach ($args as $key => $value) {
if (is_array($value)) {
return null;
}
$parts = explode('.', $key, 2);
if (count($parts) == 2) {
list($prefix, $rest) = $parts;
} else {
$prefix = null;
}
if ($prefix != 'openid') {
$obj->args->set(array(Auth_OpenID_BARE_NS, $key), $value);
} else {
$openid_args[$rest] = $value;
}
}
if ($obj->_fromOpenIDArgs($openid_args)) {
return $obj;
} else {
return null;
}
}
static function fromOpenIDArgs($openid_args)
{
// Takes an array.
// Construct a Message from a parsed KVForm message
$obj = new Auth_OpenID_Message();
if ($obj->_fromOpenIDArgs($openid_args)) {
return $obj;
} else {
return null;
}
}
/**
* @access private
*/
function _fromOpenIDArgs($openid_args)
{
global $Auth_OpenID_registered_aliases;
// Takes an Auth_OpenID_Mapping instance OR an array.
if (!Auth_OpenID_Mapping::isA($openid_args)) {
$openid_args = new Auth_OpenID_Mapping($openid_args);
}
$ns_args = array();
// Resolve namespaces
foreach ($openid_args->items() as $pair) {
list($rest, $value) = $pair;
$parts = explode('.', $rest, 2);
if (count($parts) == 2) {
list($ns_alias, $ns_key) = $parts;
} else {
$ns_alias = Auth_OpenID_NULL_NAMESPACE;
$ns_key = $rest;
}
if ($ns_alias == 'ns') {
if ($this->namespaces->addAlias($value, $ns_key) === null) {
return false;
}
} else if (($ns_alias == Auth_OpenID_NULL_NAMESPACE) &&
($ns_key == 'ns')) {
// null namespace
if ($this->setOpenIDNamespace($value, false) === false) {
return false;
}
} else {
$ns_args[] = array($ns_alias, $ns_key, $value);
}
}
if (!$this->getOpenIDNamespace()) {
if ($this->setOpenIDNamespace(Auth_OpenID_OPENID1_NS, true) ===
false) {
return false;
}
}
// Actually put the pairs into the appropriate namespaces
foreach ($ns_args as $triple) {
list($ns_alias, $ns_key, $value) = $triple;
$ns_uri = $this->namespaces->getNamespaceURI($ns_alias);
if ($ns_uri === null) {
$ns_uri = $this->_getDefaultNamespace($ns_alias);
if ($ns_uri === null) {
$ns_uri = Auth_OpenID_OPENID_NS;
$ns_key = sprintf('%s.%s', $ns_alias, $ns_key);
} else {
$this->namespaces->addAlias($ns_uri, $ns_alias, true);
}
}
$this->setArg($ns_uri, $ns_key, $value);
}
return true;
}
function _getDefaultNamespace($mystery_alias)
{
global $Auth_OpenID_registered_aliases;
if ($this->isOpenID1()) {
return @$Auth_OpenID_registered_aliases[$mystery_alias];
}
return null;
}
function setOpenIDNamespace($openid_ns_uri, $implicit)
{
if (!in_array($openid_ns_uri, $this->allowed_openid_namespaces)) {
Auth_OpenID::log('Invalid null namespace: "%s"', $openid_ns_uri);
return false;
}
$succeeded = $this->namespaces->addAlias($openid_ns_uri,
Auth_OpenID_NULL_NAMESPACE,
$implicit);
if ($succeeded === false) {
return false;
}
$this->_openid_ns_uri = $openid_ns_uri;
return true;
}
function getOpenIDNamespace()
{
return $this->_openid_ns_uri;
}
static function fromKVForm($kvform_string)
{
// Create a Message from a KVForm string
return Auth_OpenID_Message::fromOpenIDArgs(
Auth_OpenID_KVForm::toArray($kvform_string));
}
function copy()
{
return $this;
}
function toPostArgs()
{
// Return all arguments with openid. in front of namespaced
// arguments.
$args = array();
// Add namespace definitions to the output
foreach ($this->namespaces->iteritems() as $pair) {
list($ns_uri, $alias) = $pair;
if ($this->namespaces->isImplicit($ns_uri)) {
continue;
}
if ($alias == Auth_OpenID_NULL_NAMESPACE) {
$ns_key = 'openid.ns';
} else {
$ns_key = 'openid.ns.' . $alias;
}
$args[$ns_key] = $ns_uri;
}
foreach ($this->args->items() as $pair) {
list($ns_parts, $value) = $pair;
list($ns_uri, $ns_key) = $ns_parts;
$key = $this->getKey($ns_uri, $ns_key);
$args[$key] = $value;
}
return $args;
}
function toArgs()
{
// Return all namespaced arguments, failing if any
// non-namespaced arguments exist.
$post_args = $this->toPostArgs();
$kvargs = array();
foreach ($post_args as $k => $v) {
if (strpos($k, 'openid.') !== 0) {
// raise ValueError(
// 'This message can only be encoded as a POST, because it '
// 'contains arguments that are not prefixed with "openid."')
return null;
} else {
$kvargs[substr($k, 7)] = $v;
}
}
return $kvargs;
}
function toFormMarkup($action_url, $form_tag_attrs = null,
$submit_text = "Continue")
{
$form = "<form accept-charset=\"UTF-8\" ".
"enctype=\"application/x-www-form-urlencoded\"";
if (!$form_tag_attrs) {
$form_tag_attrs = array();
}
$form_tag_attrs['action'] = $action_url;
$form_tag_attrs['method'] = 'post';
unset($form_tag_attrs['enctype']);
unset($form_tag_attrs['accept-charset']);
if ($form_tag_attrs) {
foreach ($form_tag_attrs as $name => $attr) {
$form .= sprintf(" %s=\"%s\"", $name, $attr);
}
}
$form .= ">\n";
foreach ($this->toPostArgs() as $name => $value) {
$form .= sprintf(
"<input type=\"hidden\" name=\"%s\" value=\"%s\" />\n",
$name, urldecode($value));
}
$form .= sprintf("<input type=\"submit\" value=\"%s\" />\n",
$submit_text);
$form .= "</form>\n";
return $form;
}
function toURL($base_url)
{
// Generate a GET URL with the parameters in this message
// attached as query parameters.
return Auth_OpenID::appendArgs($base_url, $this->toPostArgs());
}
function toKVForm()
{
// Generate a KVForm string that contains the parameters in
// this message. This will fail if the message contains
// arguments outside of the 'openid.' prefix.
return Auth_OpenID_KVForm::fromArray($this->toArgs());
}
function toURLEncoded()
{
// Generate an x-www-urlencoded string
$args = array();
foreach ($this->toPostArgs() as $k => $v) {
$args[] = array($k, $v);
}
sort($args);
return Auth_OpenID::httpBuildQuery($args);
}
/**
* @access private
*/
function _fixNS($namespace)
{
// Convert an input value into the internally used values of
// this object
if ($namespace == Auth_OpenID_OPENID_NS) {
if ($this->_openid_ns_uri === null) {
return new Auth_OpenID_FailureResponse(null,
'OpenID namespace not set');
} else {
$namespace = $this->_openid_ns_uri;
}
}
if (($namespace != Auth_OpenID_BARE_NS) &&
(!is_string($namespace))) {
//TypeError
$err_msg = sprintf("Namespace must be Auth_OpenID_BARE_NS, ".
"Auth_OpenID_OPENID_NS or a string. got %s",
print_r($namespace, true));
return new Auth_OpenID_FailureResponse(null, $err_msg);
}
if (($namespace != Auth_OpenID_BARE_NS) &&
(strpos($namespace, ':') === false)) {
// fmt = 'OpenID 2.0 namespace identifiers SHOULD be URIs. Got %r'
// warnings.warn(fmt % (namespace,), DeprecationWarning)
if ($namespace == 'sreg') {
// fmt = 'Using %r instead of "sreg" as namespace'
// warnings.warn(fmt % (SREG_URI,), DeprecationWarning,)
return Auth_OpenID_SREG_URI;
}
}
return $namespace;
}
function hasKey($namespace, $ns_key)
{
$namespace = $this->_fixNS($namespace);
if (Auth_OpenID::isFailure($namespace)) {
// XXX log me
return false;
} else {
return $this->args->contains(array($namespace, $ns_key));
}
}
function getKey($namespace, $ns_key)
{
// Get the key for a particular namespaced argument
$namespace = $this->_fixNS($namespace);
if (Auth_OpenID::isFailure($namespace)) {
return $namespace;
}
if ($namespace == Auth_OpenID_BARE_NS) {
return $ns_key;
}
$ns_alias = $this->namespaces->getAlias($namespace);
// No alias is defined, so no key can exist
if ($ns_alias === null) {
return null;
}
if ($ns_alias == Auth_OpenID_NULL_NAMESPACE) {
$tail = $ns_key;
} else {
$tail = sprintf('%s.%s', $ns_alias, $ns_key);
}
return 'openid.' . $tail;
}
function getArg($namespace, $key, $default = null)
{
// Get a value for a namespaced key.
$namespace = $this->_fixNS($namespace);
if (Auth_OpenID::isFailure($namespace)) {
return $namespace;
} else {
if ((!$this->args->contains(array($namespace, $key))) &&
($default == Auth_OpenID_NO_DEFAULT)) {
$err_msg = sprintf("Namespace %s missing required field %s",
$namespace, $key);
return new Auth_OpenID_FailureResponse(null, $err_msg);
} else {
return $this->args->get(array($namespace, $key), $default);
}
}
}
function getArgs($namespace)
{
// Get the arguments that are defined for this namespace URI
$namespace = $this->_fixNS($namespace);
if (Auth_OpenID::isFailure($namespace)) {
return $namespace;
} else {
$stuff = array();
foreach ($this->args->items() as $pair) {
list($key, $value) = $pair;
list($pair_ns, $ns_key) = $key;
if ($pair_ns == $namespace) {
$stuff[$ns_key] = $value;
}
}
return $stuff;
}
}
function updateArgs($namespace, $updates)
{
// Set multiple key/value pairs in one call
$namespace = $this->_fixNS($namespace);
if (Auth_OpenID::isFailure($namespace)) {
return $namespace;
} else {
foreach ($updates as $k => $v) {
$this->setArg($namespace, $k, $v);
}
return true;
}
}
function setArg($namespace, $key, $value)
{
// Set a single argument in this namespace
$namespace = $this->_fixNS($namespace);
if (Auth_OpenID::isFailure($namespace)) {
return $namespace;
} else {
$this->args->set(array($namespace, $key), $value);
if ($namespace !== Auth_OpenID_BARE_NS) {
$this->namespaces->add($namespace);
}
return true;
}
}
function delArg($namespace, $key)
{
$namespace = $this->_fixNS($namespace);
if (Auth_OpenID::isFailure($namespace)) {
return $namespace;
} else {
return $this->args->del(array($namespace, $key));
}
}
function getAliasedArg($aliased_key, $default = null)
{
if ($aliased_key == 'ns') {
// Return the namespace URI for the OpenID namespace
return $this->getOpenIDNamespace();
}
$parts = explode('.', $aliased_key, 2);
if (count($parts) != 2) {
$ns = null;
} else {
list($alias, $key) = $parts;
if ($alias == 'ns') {
// Return the namespace URI for a namespace alias
// parameter.
return $this->namespaces->getNamespaceURI($key);
} else {
$ns = $this->namespaces->getNamespaceURI($alias);
}
}
if ($ns === null) {
$key = $aliased_key;
$ns = $this->getOpenIDNamespace();
}
return $this->getArg($ns, $key, $default);
}
}

View file

@ -23,27 +23,27 @@ class Auth_OpenID_MySQLStore extends Auth_OpenID_SQLStore {
function setSQL()
{
$this->sql['nonce_table'] =
"CREATE TABLE %s (nonce CHAR(8) UNIQUE PRIMARY KEY, ".
"expires INTEGER) TYPE=InnoDB";
"CREATE TABLE %s (\n".
" server_url VARCHAR(2047) NOT NULL,\n".
" timestamp INTEGER NOT NULL,\n".
" salt CHAR(40) NOT NULL,\n".
" UNIQUE (server_url(255), timestamp, salt)\n".
") ENGINE=InnoDB";
$this->sql['assoc_table'] =
"CREATE TABLE %s (server_url BLOB, handle VARCHAR(255), ".
"secret BLOB, issued INTEGER, lifetime INTEGER, ".
"assoc_type VARCHAR(64), PRIMARY KEY (server_url(255), handle)) ".
"TYPE=InnoDB";
$this->sql['settings_table'] =
"CREATE TABLE %s (setting VARCHAR(128) UNIQUE PRIMARY KEY, ".
"value BLOB) TYPE=InnoDB";
$this->sql['create_auth'] =
"INSERT INTO %s VALUES ('auth_key', !)";
$this->sql['get_auth'] =
"SELECT value FROM %s WHERE setting = 'auth_key'";
"CREATE TABLE %s (\n".
" server_url BLOB NOT NULL,\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".
") ENGINE=InnoDB";
$this->sql['set_assoc'] =
"REPLACE INTO %s VALUES (?, ?, !, ?, ?, ?)";
"REPLACE INTO %s (server_url, handle, secret, issued,\n".
" lifetime, assoc_type) VALUES (?, ?, !, ?, ?, ?)";
$this->sql['get_assocs'] =
"SELECT handle, secret, issued, lifetime, assoc_type FROM %s ".
@ -57,13 +57,13 @@ class Auth_OpenID_MySQLStore extends Auth_OpenID_SQLStore {
"DELETE FROM %s WHERE server_url = ? AND handle = ?";
$this->sql['add_nonce'] =
"REPLACE INTO %s (nonce, expires) VALUES (?, ?)";
"INSERT INTO %s (server_url, timestamp, salt) VALUES (?, ?, ?)";
$this->sql['get_nonce'] =
"SELECT * FROM %s WHERE nonce = ?";
$this->sql['clean_nonce'] =
"DELETE FROM %s WHERE timestamp < ?";
$this->sql['remove_nonce'] =
"DELETE FROM %s WHERE nonce = ?";
$this->sql['clean_assoc'] =
"DELETE FROM %s WHERE issued + lifetime < ?";
}
/**
@ -75,4 +75,3 @@ class Auth_OpenID_MySQLStore extends Auth_OpenID_SQLStore {
}
}
?>

View file

@ -0,0 +1,108 @@
<?php
/**
* Nonce-related functionality.
*
* @package OpenID
*/
/**
* Need CryptUtil to generate random strings.
*/
require_once 'Auth/OpenID/CryptUtil.php';
/**
* This is the characters that the nonces are made from.
*/
define('Auth_OpenID_Nonce_CHRS',"abcdefghijklmnopqrstuvwxyz" .
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
// Keep nonces for five hours (allow five hours for the combination of
// request time and clock skew). This is probably way more than is
// necessary, but there is not much overhead in storing nonces.
global $Auth_OpenID_SKEW;
$Auth_OpenID_SKEW = 60 * 60 * 5;
define('Auth_OpenID_Nonce_REGEX',
'/(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z(.*)/');
define('Auth_OpenID_Nonce_TIME_FMT',
'%Y-%m-%dT%H:%M:%SZ');
function Auth_OpenID_splitNonce($nonce_string)
{
// Extract a timestamp from the given nonce string
$result = preg_match(Auth_OpenID_Nonce_REGEX, $nonce_string, $matches);
if ($result != 1 || count($matches) != 8) {
return null;
}
list($unused,
$tm_year,
$tm_mon,
$tm_mday,
$tm_hour,
$tm_min,
$tm_sec,
$uniquifier) = $matches;
$timestamp =
@gmmktime($tm_hour, $tm_min, $tm_sec, $tm_mon, $tm_mday, $tm_year);
if ($timestamp === false || $timestamp < 0) {
return null;
}
return array($timestamp, $uniquifier);
}
function Auth_OpenID_checkTimestamp($nonce_string,
$allowed_skew = null,
$now = null)
{
// Is the timestamp that is part of the specified nonce string
// within the allowed clock-skew of the current time?
global $Auth_OpenID_SKEW;
if ($allowed_skew === null) {
$allowed_skew = $Auth_OpenID_SKEW;
}
$parts = Auth_OpenID_splitNonce($nonce_string);
if ($parts == null) {
return false;
}
if ($now === null) {
$now = time();
}
$stamp = $parts[0];
// Time after which we should not use the nonce
$past = $now - $allowed_skew;
// Time that is too far in the future for us to allow
$future = $now + $allowed_skew;
// the stamp is not too far in the future and is not too far
// in the past
return (($past <= $stamp) && ($stamp <= $future));
}
function Auth_OpenID_mkNonce($when = null)
{
// Generate a nonce with the current timestamp
$salt = Auth_OpenID_CryptUtil::randomString(
6, Auth_OpenID_Nonce_CHRS);
if ($when === null) {
// It's safe to call time() with no arguments; it returns a
// GMT unix timestamp on PHP 4 and PHP 5. gmmktime() with no
// args returns a local unix timestamp on PHP 4, so don't use
// that.
$when = time();
}
$time_str = gmstrftime(Auth_OpenID_Nonce_TIME_FMT, $when);
return $time_str . $salt;
}

View file

@ -0,0 +1,300 @@
<?php
/**
* An implementation of the OpenID Provider Authentication Policy
* Extension 1.0
*
* See:
* http://openid.net/developers/specs/
*/
require_once "Auth/OpenID/Extension.php";
define('Auth_OpenID_PAPE_NS_URI',
"http://specs.openid.net/extensions/pape/1.0");
define('PAPE_AUTH_MULTI_FACTOR_PHYSICAL',
'http://schemas.openid.net/pape/policies/2007/06/multi-factor-physical');
define('PAPE_AUTH_MULTI_FACTOR',
'http://schemas.openid.net/pape/policies/2007/06/multi-factor');
define('PAPE_AUTH_PHISHING_RESISTANT',
'http://schemas.openid.net/pape/policies/2007/06/phishing-resistant');
define('PAPE_TIME_VALIDATOR',
'/^[0-9]{4,4}-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9]Z$/');
/**
* A Provider Authentication Policy request, sent from a relying party
* to a provider
*
* preferred_auth_policies: The authentication policies that
* the relying party prefers
*
* max_auth_age: The maximum time, in seconds, that the relying party
* wants to allow to have elapsed before the user must re-authenticate
*/
class Auth_OpenID_PAPE_Request extends Auth_OpenID_Extension {
var $ns_alias = 'pape';
var $ns_uri = Auth_OpenID_PAPE_NS_URI;
function Auth_OpenID_PAPE_Request($preferred_auth_policies=null,
$max_auth_age=null)
{
if ($preferred_auth_policies === null) {
$preferred_auth_policies = array();
}
$this->preferred_auth_policies = $preferred_auth_policies;
$this->max_auth_age = $max_auth_age;
}
/**
* Add an acceptable authentication policy URI to this request
*
* This method is intended to be used by the relying party to add
* acceptable authentication types to the request.
*
* policy_uri: The identifier for the preferred type of
* authentication.
*/
function addPolicyURI($policy_uri)
{
if (!in_array($policy_uri, $this->preferred_auth_policies)) {
$this->preferred_auth_policies[] = $policy_uri;
}
}
function getExtensionArgs()
{
$ns_args = array(
'preferred_auth_policies' =>
implode(' ', $this->preferred_auth_policies)
);
if ($this->max_auth_age !== null) {
$ns_args['max_auth_age'] = strval($this->max_auth_age);
}
return $ns_args;
}
/**
* Instantiate a Request object from the arguments in a checkid_*
* OpenID message
*/
static function fromOpenIDRequest($request)
{
$obj = new Auth_OpenID_PAPE_Request();
$args = $request->message->getArgs(Auth_OpenID_PAPE_NS_URI);
if ($args === null || $args === array()) {
return null;
}
$obj->parseExtensionArgs($args);
return $obj;
}
/**
* Set the state of this request to be that expressed in these
* PAPE arguments
*
* @param args: The PAPE arguments without a namespace
*/
function parseExtensionArgs($args)
{
// preferred_auth_policies is a space-separated list of policy
// URIs
$this->preferred_auth_policies = array();
$policies_str = Auth_OpenID::arrayGet($args, 'preferred_auth_policies');
if ($policies_str) {
foreach (explode(' ', $policies_str) as $uri) {
if (!in_array($uri, $this->preferred_auth_policies)) {
$this->preferred_auth_policies[] = $uri;
}
}
}
// max_auth_age is base-10 integer number of seconds
$max_auth_age_str = Auth_OpenID::arrayGet($args, 'max_auth_age');
if ($max_auth_age_str) {
$this->max_auth_age = Auth_OpenID::intval($max_auth_age_str);
} else {
$this->max_auth_age = null;
}
}
/**
* Given a list of authentication policy URIs that a provider
* supports, this method returns the subsequence of those types
* that are preferred by the relying party.
*
* @param supported_types: A sequence of authentication policy
* type URIs that are supported by a provider
*
* @return array The sub-sequence of the supported types that are
* preferred by the relying party. This list will be ordered in
* the order that the types appear in the supported_types
* sequence, and may be empty if the provider does not prefer any
* of the supported authentication types.
*/
function preferredTypes($supported_types)
{
$result = array();
foreach ($supported_types as $st) {
if (in_array($st, $this->preferred_auth_policies)) {
$result[] = $st;
}
}
return $result;
}
}
/**
* A Provider Authentication Policy response, sent from a provider to
* a relying party
*/
class Auth_OpenID_PAPE_Response extends Auth_OpenID_Extension {
var $ns_alias = 'pape';
var $ns_uri = Auth_OpenID_PAPE_NS_URI;
function Auth_OpenID_PAPE_Response($auth_policies=null, $auth_time=null,
$nist_auth_level=null)
{
if ($auth_policies) {
$this->auth_policies = $auth_policies;
} else {
$this->auth_policies = array();
}
$this->auth_time = $auth_time;
$this->nist_auth_level = $nist_auth_level;
}
/**
* Add a authentication policy to this response
*
* This method is intended to be used by the provider to add a
* policy that the provider conformed to when authenticating the
* user.
*
* @param policy_uri: The identifier for the preferred type of
* authentication.
*/
function addPolicyURI($policy_uri)
{
if (!in_array($policy_uri, $this->auth_policies)) {
$this->auth_policies[] = $policy_uri;
}
}
/**
* Create an Auth_OpenID_PAPE_Response object from a successful
* OpenID library response.
*
* @param success_response $success_response A SuccessResponse
* from Auth_OpenID_Consumer::complete()
*
* @returns: A provider authentication policy response from the
* data that was supplied with the id_res response.
*/
static function fromSuccessResponse($success_response)
{
$obj = new Auth_OpenID_PAPE_Response();
// PAPE requires that the args be signed.
$args = $success_response->getSignedNS(Auth_OpenID_PAPE_NS_URI);
if ($args === null || $args === array()) {
return null;
}
$result = $obj->parseExtensionArgs($args);
if ($result === false) {
return null;
} else {
return $obj;
}
}
/**
* Parse the provider authentication policy arguments into the
* internal state of this object
*
* @param args: unqualified provider authentication policy
* arguments
*
* @param strict: Whether to return false when bad data is
* encountered
*
* @return null The data is parsed into the internal fields of
* this object.
*/
function parseExtensionArgs($args, $strict=false)
{
$policies_str = Auth_OpenID::arrayGet($args, 'auth_policies');
if ($policies_str && $policies_str != "none") {
$this->auth_policies = explode(" ", $policies_str);
}
$nist_level_str = Auth_OpenID::arrayGet($args, 'nist_auth_level');
if ($nist_level_str !== null) {
$nist_level = Auth_OpenID::intval($nist_level_str);
if ($nist_level === false) {
if ($strict) {
return false;
} else {
$nist_level = null;
}
}
if (0 <= $nist_level && $nist_level < 5) {
$this->nist_auth_level = $nist_level;
} else if ($strict) {
return false;
}
}
$auth_time = Auth_OpenID::arrayGet($args, 'auth_time');
if ($auth_time !== null) {
if (preg_match(PAPE_TIME_VALIDATOR, $auth_time)) {
$this->auth_time = $auth_time;
} else if ($strict) {
return false;
}
}
}
function getExtensionArgs()
{
$ns_args = array();
if (count($this->auth_policies) > 0) {
$ns_args['auth_policies'] = implode(' ', $this->auth_policies);
} else {
$ns_args['auth_policies'] = 'none';
}
if ($this->nist_auth_level !== null) {
if (!in_array($this->nist_auth_level, range(0, 4), true)) {
return false;
}
$ns_args['nist_auth_level'] = strval($this->nist_auth_level);
}
if ($this->auth_time !== null) {
if (!preg_match(PAPE_TIME_VALIDATOR, $this->auth_time)) {
return false;
}
$ns_args['auth_time'] = $this->auth_time;
}
return $ns_args;
}
}

View file

@ -75,8 +75,8 @@
* @access private
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
@ -101,10 +101,13 @@ class Auth_OpenID_Parse {
* Starts with the tag name at a word boundary, where the tag name
* is not a namespace
*/
var $_tag_expr = "<%s\b(?!:)([^>]*?)(?:\/>|>(.*?)(?:<\/?%s\s*>|\Z))";
var $_tag_expr = "<%s\b(?!:)([^>]*?)(?:\/>|>(.*)(?:<\/?%s\s*>|\Z))";
var $_attr_find = '\b(\w+)=("[^"]*"|\'[^\']*\'|[^\'"\s\/<>]+)';
var $_open_tag_expr = "<%s\b";
var $_close_tag_expr = "<((\/%s\b)|(%s[^>\/]*\/))>";
function Auth_OpenID_Parse()
{
$this->_link_find = sprintf("/<link\b(?!:)([^>]*)(?!<)>/%s",
@ -136,6 +139,8 @@ class Auth_OpenID_Parse {
*/
function tagMatcher($tag_name, $close_tags = null)
{
$expr = $this->_tag_expr;
if ($close_tags) {
$options = implode("|", array_merge(array($tag_name), $close_tags));
$closer = sprintf("(?:%s)", $options);
@ -143,18 +148,49 @@ class Auth_OpenID_Parse {
$closer = $tag_name;
}
$expr = sprintf($this->_tag_expr, $tag_name, $closer);
$expr = sprintf($expr, $tag_name, $closer);
return sprintf("/%s/%s", $expr, $this->_re_flags);
}
function htmlFind()
function openTag($tag_name)
{
return $this->tagMatcher('html');
$expr = sprintf($this->_open_tag_expr, $tag_name);
return sprintf("/%s/%s", $expr, $this->_re_flags);
}
function closeTag($tag_name)
{
$expr = sprintf($this->_close_tag_expr, $tag_name, $tag_name);
return sprintf("/%s/%s", $expr, $this->_re_flags);
}
function htmlBegin($s)
{
$matches = array();
$result = preg_match($this->openTag('html'), $s,
$matches, PREG_OFFSET_CAPTURE);
if ($result === false || !$matches) {
return false;
}
// Return the offset of the first match.
return $matches[0][1];
}
function htmlEnd($s)
{
$matches = array();
$result = preg_match($this->closeTag('html'), $s,
$matches, PREG_OFFSET_CAPTURE);
if ($result === false || !$matches) {
return false;
}
// Return the offset of the first match.
return $matches[count($matches) - 1][1];
}
function headFind()
{
return $this->tagMatcher('head', array('body'));
return $this->tagMatcher('head', array('body', 'html'));
}
function replaceEntities($str)
@ -179,11 +215,31 @@ class Auth_OpenID_Parse {
return $str;
}
}
function match($regexp, $text, &$match)
{
if (!is_callable('mb_ereg_search_init')) {
return preg_match($regexp, $text, $match);
}
$regexp = substr($regexp, 1, strlen($regexp) - 2 - strlen($this->_re_flags));
mb_ereg_search_init($text);
if (!mb_ereg_search($regexp)) {
return false;
}
$match = mb_ereg_search_getregs();
return true;
}
/**
* Find all link tags in a string representing a HTML document and
* return a list of their attributes.
*
* @todo This is quite ineffective and may fail with the default
* pcre.backtrack_limit of 100000 in PHP 5.2, if $html is big.
* It should rather use stripos (in PHP5) or strpos()+strtoupper()
* in PHP4 to manage this.
*
* @param string $html The text to parse
* @return array $list An array of arrays of attributes, one for each
* link tag
@ -194,25 +250,37 @@ class Auth_OpenID_Parse {
"",
$html);
// Try to find the <HTML> tag.
$html_re = $this->htmlFind();
$html_matches = array();
if (!preg_match($html_re, $stripped, $html_matches)) {
$html_begin = $this->htmlBegin($stripped);
$html_end = $this->htmlEnd($stripped);
if ($html_begin === false) {
return array();
}
if ($html_end === false) {
$html_end = strlen($stripped);
}
$stripped = substr($stripped, $html_begin,
$html_end - $html_begin);
// Workaround to prevent PREG_BACKTRACK_LIMIT_ERROR:
$old_btlimit = ini_set( 'pcre.backtrack_limit', -1 );
// Try to find the <HEAD> tag.
$head_re = $this->headFind();
$head_matches = array();
if (!preg_match($head_re, $html_matches[0], $head_matches)) {
return array();
$head_match = array();
if (!$this->match($head_re, $stripped, $head_match)) {
ini_set( 'pcre.backtrack_limit', $old_btlimit );
return array();
}
$link_data = array();
$link_matches = array();
if (!preg_match_all($this->_link_find, $head_matches[0],
if (!preg_match_all($this->_link_find, $head_match[0],
$link_matches)) {
ini_set( 'pcre.backtrack_limit', $old_btlimit );
return array();
}
@ -230,6 +298,7 @@ class Auth_OpenID_Parse {
$link_data[] = $link_attrs;
}
ini_set( 'pcre.backtrack_limit', $old_btlimit );
return $link_data;
}
@ -287,22 +356,22 @@ class Auth_OpenID_Parse {
}
}
function Auth_OpenID_legacy_discover($html_text)
function Auth_OpenID_legacy_discover($html_text, $server_rel,
$delegate_rel)
{
$p = new Auth_OpenID_Parse();
$link_attrs = $p->parseLinkAttrs($html_text);
$server_url = $p->findFirstHref($link_attrs,
'openid.server');
$server_rel);
if ($server_url === null) {
return false;
} else {
$delegate_url = $p->findFirstHref($link_attrs,
'openid.delegate');
$delegate_rel);
return array($delegate_url, $server_url);
}
}
?>

View file

@ -23,27 +23,22 @@ class Auth_OpenID_PostgreSQLStore extends Auth_OpenID_SQLStore {
function setSQL()
{
$this->sql['nonce_table'] =
"CREATE TABLE %s (nonce CHAR(8) UNIQUE PRIMARY KEY, ".
"expires INTEGER)";
"CREATE TABLE %s (server_url VARCHAR(2047) NOT NULL, ".
"timestamp INTEGER NOT NULL, ".
"salt CHAR(40) NOT NULL, ".
"UNIQUE (server_url, timestamp, salt))";
$this->sql['assoc_table'] =
"CREATE TABLE %s (server_url VARCHAR(2047), handle VARCHAR(255), ".
"secret BYTEA, issued INTEGER, lifetime INTEGER, ".
"assoc_type VARCHAR(64), PRIMARY KEY (server_url, handle), ".
"CREATE TABLE %s (server_url VARCHAR(2047) NOT NULL, ".
"handle VARCHAR(255) NOT NULL, ".
"secret BYTEA NOT NULL, ".
"issued INTEGER NOT NULL, ".
"lifetime INTEGER NOT NULL, ".
"assoc_type VARCHAR(64) NOT NULL, ".
"PRIMARY KEY (server_url, handle), ".
"CONSTRAINT secret_length_constraint CHECK ".
"(LENGTH(secret) <= 128))";
$this->sql['settings_table'] =
"CREATE TABLE %s (setting VARCHAR(128) UNIQUE PRIMARY KEY, ".
"value BYTEA, ".
"CONSTRAINT value_length_constraint CHECK (LENGTH(value) <= 20))";
$this->sql['create_auth'] =
"INSERT INTO %s VALUES ('auth_key', '!')";
$this->sql['get_auth'] =
"SELECT value FROM %s WHERE setting = 'auth_key'";
$this->sql['set_assoc'] =
array(
'insert_assoc' => "INSERT INTO %s (server_url, handle, ".
@ -66,17 +61,15 @@ class Auth_OpenID_PostgreSQLStore extends Auth_OpenID_SQLStore {
"DELETE FROM %s WHERE server_url = ? AND handle = ?";
$this->sql['add_nonce'] =
array(
'insert_nonce' => "INSERT INTO %s (nonce, expires) VALUES ".
"(?, ?)",
'update_nonce' => "UPDATE %s SET expires = ? WHERE nonce = ?"
);
"INSERT INTO %s (server_url, timestamp, salt) VALUES ".
"(?, ?, ?)"
;
$this->sql['get_nonce'] =
"SELECT * FROM %s WHERE nonce = ?";
$this->sql['clean_nonce'] =
"DELETE FROM %s WHERE timestamp < ?";
$this->sql['remove_nonce'] =
"DELETE FROM %s WHERE nonce = ?";
$this->sql['clean_assoc'] =
"DELETE FROM %s WHERE issued + lifetime < ?";
}
/**
@ -100,22 +93,6 @@ class Auth_OpenID_PostgreSQLStore extends Auth_OpenID_SQLStore {
}
}
/**
* @access private
*/
function _add_nonce($nonce, $expires)
{
if ($this->_get_nonce($nonce)) {
return $this->resultToBool($this->connection->query(
$this->sql['add_nonce']['update_nonce'],
array($expires, $nonce)));
} else {
return $this->resultToBool($this->connection->query(
$this->sql['add_nonce']['insert_nonce'],
array($nonce, $expires)));
}
}
/**
* @access private
*/
@ -133,4 +110,3 @@ class Auth_OpenID_PostgreSQLStore extends Auth_OpenID_SQLStore {
}
}
?>

View file

@ -9,33 +9,33 @@
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* Require the PEAR DB module because we'll need it for the SQL-based
* stores implemented here. We silence any errors from the inclusion
* because it might not be present, and a user of the SQL stores may
* supply an Auth_OpenID_DatabaseConnection instance that implements
* its own storage.
*/
global $__Auth_OpenID_PEAR_AVAILABLE;
$__Auth_OpenID_PEAR_AVAILABLE = @include_once 'DB.php';
/**
* @access private
*/
require_once 'Auth/OpenID/Interface.php';
require_once 'Auth/OpenID/Nonce.php';
/**
* @access private
*/
require_once 'Auth/OpenID.php';
/**
* @access private
*/
require_once 'Auth/OpenID/Nonce.php';
/**
* This is the parent class for the SQL stores, which contains the
* logic common to all of the SQL stores.
*
* The table names used are determined by the class variables
* settings_table_name, associations_table_name, and
* nonces_table_name. To change the name of the tables used, pass new
* table names into the constructor.
* 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.
@ -67,10 +67,6 @@ class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore {
* connection handle or an instance of a subclass of
* Auth_OpenID_DatabaseConnection.
*
* @param string $settings_table This is an optional parameter to
* specify the name of the table used for this store's settings.
* The default value is 'oid_settings'.
*
* @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'.
@ -79,13 +75,10 @@ class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore {
* the name of the table used for storing nonces. The default
* value is 'oid_nonces'.
*/
function Auth_OpenID_SQLStore($connection, $settings_table = null,
function Auth_OpenID_SQLStore($connection,
$associations_table = null,
$nonces_table = null)
{
global $__Auth_OpenID_PEAR_AVAILABLE;
$this->settings_table_name = "oid_settings";
$this->associations_table_name = "oid_associations";
$this->nonces_table_name = "oid_nonces";
@ -108,14 +101,10 @@ class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore {
// constant, so only try to use it if PEAR is present. Note
// that Auth_Openid_Databaseconnection instances need not
// implement ::setFetchMode for this reason.
if ($__Auth_OpenID_PEAR_AVAILABLE) {
if (is_subclass_of($this->connection, 'db_common')) {
$this->connection->setFetchMode(DB_FETCHMODE_ASSOC);
}
if ($settings_table) {
$this->settings_table_name = $settings_table;
}
if ($associations_table) {
$this->associations_table_name = $associations_table;
}
@ -166,8 +155,9 @@ class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore {
function tableExists($table_name)
{
return !$this->isError(
$this->connection->query("SELECT * FROM %s LIMIT 0",
$table_name));
$this->connection->query(
sprintf("SELECT * FROM %s LIMIT 0",
$table_name)));
}
/**
@ -213,9 +203,6 @@ class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore {
$this->connection->query(sprintf("DELETE FROM %s",
$this->nonces_table_name));
$this->connection->query(sprintf("DELETE FROM %s",
$this->settings_table_name));
}
/**
@ -229,16 +216,10 @@ class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore {
$required_sql_keys = array(
'nonce_table',
'assoc_table',
'settings_table',
'get_auth',
'create_auth',
'set_assoc',
'get_assoc',
'get_assocs',
'remove_assoc',
'add_nonce',
'get_nonce',
'remove_nonce'
'remove_assoc'
);
foreach ($required_sql_keys as $key) {
@ -262,8 +243,7 @@ class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore {
'value' => $this->nonces_table_name,
'keys' => array('nonce_table',
'add_nonce',
'get_nonce',
'remove_nonce')
'clean_nonce')
),
array(
'value' => $this->associations_table_name,
@ -271,13 +251,8 @@ class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore {
'set_assoc',
'get_assoc',
'get_assocs',
'remove_assoc')
),
array(
'value' => $this->settings_table_name,
'keys' => array('settings_table',
'get_auth',
'create_auth')
'remove_assoc',
'clean_assoc')
)
);
@ -313,10 +288,9 @@ class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore {
$this->connection->autoCommit(true);
$n = $this->create_nonce_table();
$a = $this->create_assoc_table();
$s = $this->create_settings_table();
$this->connection->autoCommit(false);
if ($n && $a && $s) {
if ($n && $a) {
return true;
} else {
return false;
@ -341,58 +315,6 @@ class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore {
return true;
}
function create_settings_table()
{
if (!$this->tableExists($this->settings_table_name)) {
$r = $this->connection->query($this->sql['settings_table']);
return $this->resultToBool($r);
}
return true;
}
/**
* @access private
*/
function _get_auth()
{
return $this->connection->getOne($this->sql['get_auth']);
}
/**
* @access private
*/
function _create_auth($str)
{
return $this->connection->query($this->sql['create_auth'],
array($str));
}
function getAuthKey()
{
$value = $this->_get_auth();
if (!$value) {
$auth_key =
Auth_OpenID_CryptUtil::randomString($this->AUTH_KEY_LEN);
$auth_key_s = $this->blobEncode($auth_key);
$this->_create_auth($auth_key_s);
} else {
$auth_key_s = $value;
$auth_key = $this->blobDecode($auth_key_s);
}
$this->connection->commit();
if (strlen($auth_key) != $this->AUTH_KEY_LEN) {
$fmt = "Expected %d-byte string for auth key. Got key of length %d";
trigger_error(sprintf($fmt, $this->AUTH_KEY_LEN, strlen($auth_key)),
E_USER_WARNING);
return null;
}
return $auth_key;
}
/**
* @access private
*/
@ -529,72 +451,29 @@ class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore {
/**
* @access private
*/
function _add_nonce($nonce, $expires)
function _add_nonce($server_url, $timestamp, $salt)
{
$sql = $this->sql['add_nonce'];
$result = $this->connection->query($sql, array($nonce, $expires));
$result = $this->connection->query($sql, array($server_url,
$timestamp,
$salt));
if ($this->isError($result)) {
$this->connection->rollback();
} else {
$this->connection->commit();
}
return $this->resultToBool($result);
}
/**
* @access private
*/
function storeNonce($nonce)
function useNonce($server_url, $timestamp, $salt)
{
if ($this->_add_nonce($nonce, time())) {
$this->connection->commit();
} else {
$this->connection->rollback();
}
}
global $Auth_OpenID_SKEW;
/**
* @access private
*/
function _get_nonce($nonce)
{
$result = $this->connection->getRow($this->sql['get_nonce'],
array($nonce));
if ($this->isError($result)) {
return null;
} else {
return $result;
}
}
/**
* @access private
*/
function _remove_nonce($nonce)
{
$this->connection->query($this->sql['remove_nonce'],
array($nonce));
}
function useNonce($nonce)
{
$row = $this->_get_nonce($nonce);
if ($row !== null) {
$nonce = $row['nonce'];
$timestamp = $row['expires'];
$nonce_age = time() - $timestamp;
if ($nonce_age > $this->max_nonce_age) {
$present = 0;
} else {
$present = 1;
}
$this->_remove_nonce($nonce);
} else {
$present = 0;
if ( abs($timestamp - time()) > $Auth_OpenID_SKEW ) {
return false;
}
$this->connection->commit();
return $present;
return $this->_add_nonce($server_url, $timestamp, $salt);
}
/**
@ -607,7 +486,7 @@ class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore {
function _octify($str)
{
$result = "";
for ($i = 0; $i < strlen($str); $i++) {
for ($i = 0; $i < Auth_OpenID::bytes($str); $i++) {
$ch = substr($str, $i, 1);
if ($ch == "\\") {
$result .= "\\\\\\\\";
@ -653,6 +532,26 @@ class Auth_OpenID_SQLStore extends Auth_OpenID_OpenIDStore {
return $result;
}
function cleanupNonces()
{
global $Auth_OpenID_SKEW;
$v = time() - $Auth_OpenID_SKEW;
$this->connection->query($this->sql['clean_nonce'], array($v));
$num = $this->connection->affectedRows();
$this->connection->commit();
return $num;
}
function cleanupAssociations()
{
$this->connection->query($this->sql['clean_assoc'],
array(time()));
$num = $this->connection->affectedRows();
$this->connection->commit();
return $num;
}
}
?>

View file

@ -20,24 +20,14 @@ class Auth_OpenID_SQLiteStore extends Auth_OpenID_SQLStore {
function setSQL()
{
$this->sql['nonce_table'] =
"CREATE TABLE %s (nonce CHAR(8) UNIQUE PRIMARY KEY, ".
"expires INTEGER)";
"CREATE TABLE %s (server_url VARCHAR(2047), timestamp INTEGER, ".
"salt CHAR(40), UNIQUE (server_url, timestamp, salt))";
$this->sql['assoc_table'] =
"CREATE TABLE %s (server_url VARCHAR(2047), handle VARCHAR(255), ".
"secret BLOB(128), issued INTEGER, lifetime INTEGER, ".
"assoc_type VARCHAR(64), PRIMARY KEY (server_url, handle))";
$this->sql['settings_table'] =
"CREATE TABLE %s (setting VARCHAR(128) UNIQUE PRIMARY KEY, ".
"value BLOB(20))";
$this->sql['create_auth'] =
"INSERT INTO %s VALUES ('auth_key', ?)";
$this->sql['get_auth'] =
"SELECT value FROM %s WHERE setting = 'auth_key'";
$this->sql['set_assoc'] =
"INSERT OR REPLACE INTO %s VALUES (?, ?, ?, ?, ?, ?)";
@ -53,14 +43,28 @@ class Auth_OpenID_SQLiteStore extends Auth_OpenID_SQLStore {
"DELETE FROM %s WHERE server_url = ? AND handle = ?";
$this->sql['add_nonce'] =
"INSERT OR REPLACE INTO %s (nonce, expires) VALUES (?, ?)";
"INSERT INTO %s (server_url, timestamp, salt) VALUES (?, ?, ?)";
$this->sql['get_nonce'] =
"SELECT * FROM %s WHERE nonce = ?";
$this->sql['clean_nonce'] =
"DELETE FROM %s WHERE timestamp < ?";
$this->sql['remove_nonce'] =
"DELETE FROM %s WHERE nonce = ?";
$this->sql['clean_assoc'] =
"DELETE FROM %s WHERE issued + lifetime < ?";
}
/**
* @access private
*/
function _add_nonce($server_url, $timestamp, $salt)
{
// PECL SQLite extensions 1.0.3 and older (1.0.3 is the
// current release at the time of this writing) have a broken
// sqlite_escape_string function that breaks when passed the
// empty string. Prefixing all strings with one character
// keeps them unique and avoids this bug. The nonce table is
// write-only, so we don't have to worry about updating other
// functions with this same bad hack.
return parent::_add_nonce('x' . $server_url, $timestamp, $salt);
}
}
?>

View file

@ -0,0 +1,521 @@
<?php
/**
* Simple registration request and response parsing and object
* representation.
*
* This module contains objects representing simple registration
* requests and responses that can be used with both OpenID relying
* parties and OpenID providers.
*
* 1. The relying party creates a request object and adds it to the
* {@link Auth_OpenID_AuthRequest} object before making the
* checkid request to the OpenID provider:
*
* $sreg_req = Auth_OpenID_SRegRequest::build(array('email'));
* $auth_request->addExtension($sreg_req);
*
* 2. The OpenID provider extracts the simple registration request
* from the OpenID request using {@link
* Auth_OpenID_SRegRequest::fromOpenIDRequest}, gets the user's
* approval and data, creates an {@link Auth_OpenID_SRegResponse}
* object and adds it to the id_res response:
*
* $sreg_req = Auth_OpenID_SRegRequest::fromOpenIDRequest(
* $checkid_request);
* // [ get the user's approval and data, informing the user that
* // the fields in sreg_response were requested ]
* $sreg_resp = Auth_OpenID_SRegResponse::extractResponse(
* $sreg_req, $user_data);
* $sreg_resp->toMessage($openid_response->fields);
*
* 3. The relying party uses {@link
* Auth_OpenID_SRegResponse::fromSuccessResponse} to extract the data
* from the OpenID response:
*
* $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse(
* $success_response);
*
* @package OpenID
*/
/**
* Import message and extension internals.
*/
require_once 'Auth/OpenID/Message.php';
require_once 'Auth/OpenID/Extension.php';
// The data fields that are listed in the sreg spec
global $Auth_OpenID_sreg_data_fields;
$Auth_OpenID_sreg_data_fields = array(
'fullname' => 'Full Name',
'nickname' => 'Nickname',
'dob' => 'Date of Birth',
'email' => 'E-mail Address',
'gender' => 'Gender',
'postcode' => 'Postal Code',
'country' => 'Country',
'language' => 'Language',
'timezone' => 'Time Zone');
/**
* Check to see that the given value is a valid simple registration
* data field name. Return true if so, false if not.
*/
function Auth_OpenID_checkFieldName($field_name)
{
global $Auth_OpenID_sreg_data_fields;
if (!in_array($field_name, array_keys($Auth_OpenID_sreg_data_fields))) {
return false;
}
return true;
}
// URI used in the wild for Yadis documents advertising simple
// registration support
define('Auth_OpenID_SREG_NS_URI_1_0', 'http://openid.net/sreg/1.0');
// URI in the draft specification for simple registration 1.1
// <http://openid.net/specs/openid-simple-registration-extension-1_1-01.html>
define('Auth_OpenID_SREG_NS_URI_1_1', 'http://openid.net/extensions/sreg/1.1');
// This attribute will always hold the preferred URI to use when
// adding sreg support to an XRDS file or in an OpenID namespace
// declaration.
define('Auth_OpenID_SREG_NS_URI', Auth_OpenID_SREG_NS_URI_1_1);
Auth_OpenID_registerNamespaceAlias(Auth_OpenID_SREG_NS_URI_1_1, 'sreg');
/**
* Does the given endpoint advertise support for simple
* registration?
*
* $endpoint: The endpoint object as returned by OpenID discovery.
* returns whether an sreg type was advertised by the endpoint
*/
function Auth_OpenID_supportsSReg($endpoint)
{
return ($endpoint->usesExtension(Auth_OpenID_SREG_NS_URI_1_1) ||
$endpoint->usesExtension(Auth_OpenID_SREG_NS_URI_1_0));
}
/**
* A base class for classes dealing with Simple Registration protocol
* messages.
*
* @package OpenID
*/
class Auth_OpenID_SRegBase extends Auth_OpenID_Extension {
/**
* Extract the simple registration namespace URI from the given
* OpenID message. Handles OpenID 1 and 2, as well as both sreg
* namespace URIs found in the wild, as well as missing namespace
* definitions (for OpenID 1)
*
* $message: The OpenID message from which to parse simple
* registration fields. This may be a request or response message.
*
* Returns the sreg namespace URI for the supplied message. The
* message may be modified to define a simple registration
* namespace.
*
* @access private
*/
static function _getSRegNS($message)
{
$alias = null;
$found_ns_uri = null;
// See if there exists an alias for one of the two defined
// simple registration types.
foreach (array(Auth_OpenID_SREG_NS_URI_1_1,
Auth_OpenID_SREG_NS_URI_1_0) as $sreg_ns_uri) {
$alias = $message->namespaces->getAlias($sreg_ns_uri);
if ($alias !== null) {
$found_ns_uri = $sreg_ns_uri;
break;
}
}
if ($alias === null) {
// There is no alias for either of the types, so try to
// add one. We default to using the modern value (1.1)
$found_ns_uri = Auth_OpenID_SREG_NS_URI_1_1;
if ($message->namespaces->addAlias(Auth_OpenID_SREG_NS_URI_1_1,
'sreg') === null) {
// An alias for the string 'sreg' already exists, but
// it's defined for something other than simple
// registration
return null;
}
}
return $found_ns_uri;
}
}
/**
* An object to hold the state of a simple registration request.
*
* required: A list of the required fields in this simple registration
* request
*
* optional: A list of the optional fields in this simple registration
* request
*
* @package OpenID
*/
class Auth_OpenID_SRegRequest extends Auth_OpenID_SRegBase {
var $ns_alias = 'sreg';
/**
* Initialize an empty simple registration request.
*/
static function build($required=null, $optional=null,
$policy_url=null,
$sreg_ns_uri=Auth_OpenID_SREG_NS_URI,
$cls='Auth_OpenID_SRegRequest')
{
$obj = new $cls();
$obj->required = array();
$obj->optional = array();
$obj->policy_url = $policy_url;
$obj->ns_uri = $sreg_ns_uri;
if ($required) {
if (!$obj->requestFields($required, true, true)) {
return null;
}
}
if ($optional) {
if (!$obj->requestFields($optional, false, true)) {
return null;
}
}
return $obj;
}
/**
* Create a simple registration request that contains the fields
* that were requested in the OpenID request with the given
* arguments
*
* $request: The OpenID authentication request from which to
* extract an sreg request.
*
* $cls: name of class to use when creating sreg request object.
* Used for testing.
*
* Returns the newly created simple registration request
*/
static function fromOpenIDRequest($request, $cls='Auth_OpenID_SRegRequest')
{
$obj = call_user_func_array(array($cls, 'build'),
array(null, null, null, Auth_OpenID_SREG_NS_URI, $cls));
// Since we're going to mess with namespace URI mapping, don't
// mutate the object that was passed in.
$m = $request->message;
$obj->ns_uri = $obj->_getSRegNS($m);
$args = $m->getArgs($obj->ns_uri);
if ($args === null || Auth_OpenID::isFailure($args)) {
return null;
}
$obj->parseExtensionArgs($args);
return $obj;
}
/**
* Parse the unqualified simple registration request parameters
* and add them to this object.
*
* This method is essentially the inverse of
* getExtensionArgs. This method restores the serialized simple
* registration request fields.
*
* If you are extracting arguments from a standard OpenID
* checkid_* request, you probably want to use fromOpenIDRequest,
* which will extract the sreg namespace and arguments from the
* OpenID request. This method is intended for cases where the
* OpenID server needs more control over how the arguments are
* parsed than that method provides.
*
* $args == $message->getArgs($ns_uri);
* $request->parseExtensionArgs($args);
*
* $args: The unqualified simple registration arguments
*
* strict: Whether requests with fields that are not defined in
* the simple registration specification should be tolerated (and
* ignored)
*/
function parseExtensionArgs($args, $strict=false)
{
foreach (array('required', 'optional') as $list_name) {
$required = ($list_name == 'required');
$items = Auth_OpenID::arrayGet($args, $list_name);
if ($items) {
foreach (explode(',', $items) as $field_name) {
if (!$this->requestField($field_name, $required, $strict)) {
if ($strict) {
return false;
}
}
}
}
}
$this->policy_url = Auth_OpenID::arrayGet($args, 'policy_url');
return true;
}
/**
* A list of all of the simple registration fields that were
* requested, whether they were required or optional.
*/
function allRequestedFields()
{
return array_merge($this->required, $this->optional);
}
/**
* Have any simple registration fields been requested?
*/
function wereFieldsRequested()
{
return count($this->allRequestedFields());
}
/**
* Was this field in the request?
*/
function contains($field_name)
{
return (in_array($field_name, $this->required) ||
in_array($field_name, $this->optional));
}
/**
* Request the specified field from the OpenID user
*
* $field_name: the unqualified simple registration field name
*
* required: whether the given field should be presented to the
* user as being a required to successfully complete the request
*
* strict: whether to raise an exception when a field is added to
* a request more than once
*/
function requestField($field_name,
$required=false, $strict=false)
{
if (!Auth_OpenID_checkFieldName($field_name)) {
return false;
}
if ($strict) {
if ($this->contains($field_name)) {
return false;
}
} else {
if (in_array($field_name, $this->required)) {
return true;
}
if (in_array($field_name, $this->optional)) {
if ($required) {
unset($this->optional[array_search($field_name,
$this->optional)]);
} else {
return true;
}
}
}
if ($required) {
$this->required[] = $field_name;
} else {
$this->optional[] = $field_name;
}
return true;
}
/**
* Add the given list of fields to the request
*
* field_names: The simple registration data fields to request
*
* required: Whether these values should be presented to the user
* as required
*
* strict: whether to raise an exception when a field is added to
* a request more than once
*/
function requestFields($field_names, $required=false, $strict=false)
{
if (!is_array($field_names)) {
return false;
}
foreach ($field_names as $field_name) {
if (!$this->requestField($field_name, $required, $strict=$strict)) {
return false;
}
}
return true;
}
/**
* Get a dictionary of unqualified simple registration arguments
* representing this request.
*
* This method is essentially the inverse of
* C{L{parseExtensionArgs}}. This method serializes the simple
* registration request fields.
*/
function getExtensionArgs()
{
$args = array();
if ($this->required) {
$args['required'] = implode(',', $this->required);
}
if ($this->optional) {
$args['optional'] = implode(',', $this->optional);
}
if ($this->policy_url) {
$args['policy_url'] = $this->policy_url;
}
return $args;
}
}
/**
* Represents the data returned in a simple registration response
* inside of an OpenID C{id_res} response. This object will be created
* by the OpenID server, added to the C{id_res} response object, and
* then extracted from the C{id_res} message by the Consumer.
*
* @package OpenID
*/
class Auth_OpenID_SRegResponse extends Auth_OpenID_SRegBase {
var $ns_alias = 'sreg';
function Auth_OpenID_SRegResponse($data=null,
$sreg_ns_uri=Auth_OpenID_SREG_NS_URI)
{
if ($data === null) {
$this->data = array();
} else {
$this->data = $data;
}
$this->ns_uri = $sreg_ns_uri;
}
/**
* Take a C{L{SRegRequest}} and a dictionary of simple
* registration values and create a C{L{SRegResponse}} object
* containing that data.
*
* request: The simple registration request object
*
* data: The simple registration data for this response, as a
* dictionary from unqualified simple registration field name to
* string (unicode) value. For instance, the nickname should be
* stored under the key 'nickname'.
*/
static function extractResponse($request, $data)
{
$obj = new Auth_OpenID_SRegResponse();
$obj->ns_uri = $request->ns_uri;
foreach ($request->allRequestedFields() as $field) {
$value = Auth_OpenID::arrayGet($data, $field);
if ($value !== null) {
$obj->data[$field] = $value;
}
}
return $obj;
}
/**
* Create a C{L{SRegResponse}} object from a successful OpenID
* library response
* (C{L{openid.consumer.consumer.SuccessResponse}}) response
* message
*
* success_response: A SuccessResponse from consumer.complete()
*
* signed_only: Whether to process only data that was
* signed in the id_res message from the server.
*
* Returns a simple registration response containing the data that
* was supplied with the C{id_res} response.
*/
static function fromSuccessResponse($success_response, $signed_only=true)
{
global $Auth_OpenID_sreg_data_fields;
$obj = new Auth_OpenID_SRegResponse();
$obj->ns_uri = $obj->_getSRegNS($success_response->message);
if ($signed_only) {
$args = $success_response->getSignedNS($obj->ns_uri);
} else {
$args = $success_response->message->getArgs($obj->ns_uri);
}
if ($args === null || Auth_OpenID::isFailure($args)) {
return null;
}
foreach ($Auth_OpenID_sreg_data_fields as $field_name => $desc) {
if (in_array($field_name, array_keys($args))) {
$obj->data[$field_name] = $args[$field_name];
}
}
return $obj;
}
function getExtensionArgs()
{
return $this->data;
}
// Read-only dictionary interface
function get($field_name, $default=null)
{
if (!Auth_OpenID_checkFieldName($field_name)) {
return null;
}
return Auth_OpenID::arrayGet($this->data, $field_name, $default);
}
function contents()
{
return $this->data;
}
}

File diff suppressed because it is too large Load diff

View file

@ -10,8 +10,8 @@
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
@ -34,4 +34,3 @@ class Auth_OpenID_ServerRequest {
}
}
?>

View file

@ -8,10 +8,12 @@
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
require_once 'Auth/OpenID/Discover.php';
/**
* A regular expression that matches a domain ending in a top-level domains.
* Used in checking trust roots for sanity.
@ -19,23 +21,66 @@
* @access private
*/
define('Auth_OpenID___TLDs',
'/\.(com|edu|gov|int|mil|net|org|biz|info|name|museum|coop|aero|ac|' .
'ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|az|ba|bb|bd|be|bf|bg|' .
'bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|' .
'cm|cn|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|' .
'fi|fj|fk|fm|fo|fr|ga|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|' .
'gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|' .
'ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|' .
'ma|mc|md|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|' .
'nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|' .
'ps|pt|pw|py|qa|re|ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|' .
'so|sr|st|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tm|tn|to|tp|tr|tt|tv|tw|tz|' .
'ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw)$/');
'/\.(ac|ad|ae|aero|af|ag|ai|al|am|an|ao|aq|ar|arpa|as|asia' .
'|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|biz|bj|bm|bn|bo|br' .
'|bs|bt|bv|bw|by|bz|ca|cat|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co' .
'|com|coop|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg' .
'|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl' .
'|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie' .
'|il|im|in|info|int|io|iq|ir|is|it|je|jm|jo|jobs|jp|ke|kg|kh' .
'|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly' .
'|ma|mc|md|me|mg|mh|mil|mk|ml|mm|mn|mo|mobi|mp|mq|mr|ms|mt' .
'|mu|museum|mv|mw|mx|my|mz|na|name|nc|ne|net|nf|ng|ni|nl|no' .
'|np|nr|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|pro|ps|pt' .
'|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl' .
'|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tel|tf|tg|th|tj|tk|tl|tm' .
'|tn|to|tp|tr|travel|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve' .
'|vg|vi|vn|vu|wf|ws|xn--0zwm56d|xn--11b5bs3a9aj6g' .
'|xn--80akhbyknj4f|xn--9t4b11yi5a|xn--deba0ad|xn--g6w251d' .
'|xn--hgbk6aj7f53bba|xn--hlcj6aya9esc7a|xn--jxalpdlp' .
'|xn--kgbechtv|xn--zckzah|ye|yt|yu|za|zm|zw)\.?$/');
define('Auth_OpenID___HostSegmentRe',
"/^(?:[-a-zA-Z0-9!$&'\\(\\)\\*+,;=._~]|%[a-zA-Z0-9]{2})*$/");
/**
* A wrapper for trust-root related functions
*/
class Auth_OpenID_TrustRoot {
/*
* Return a discovery URL for this realm.
*
* Return null if the realm could not be parsed or was not valid.
*
* @param return_to The relying party return URL of the OpenID
* authentication request
*
* @return The URL upon which relying party discovery should be
* run in order to verify the return_to URL
*/
static function buildDiscoveryURL($realm)
{
$parsed = Auth_OpenID_TrustRoot::_parse($realm);
if ($parsed === false) {
return false;
}
if ($parsed['wildcard']) {
// Use "www." in place of the star
if ($parsed['host'][0] != '.') {
return false;
}
$www_domain = 'www' . $parsed['host'];
return sprintf('%s://%s%s', $parsed['scheme'],
$www_domain, $parsed['path']);
} else {
return $parsed['unparsed'];
}
}
/**
* Parse a URL into its trust_root parts.
*
@ -48,12 +93,22 @@ class Auth_OpenID_TrustRoot {
* @return mixed $parsed Either an associative array of trust root
* parts or false if parsing failed.
*/
function _parse($trust_root)
static function _parse($trust_root)
{
$trust_root = Auth_OpenID_urinorm($trust_root);
if ($trust_root === null) {
return false;
}
if (preg_match("/:\/\/[^:]+(:\d+){2,}(\/|$)/", $trust_root)) {
return false;
}
$parts = @parse_url($trust_root);
if ($parts === false) {
return false;
}
$required_parts = array('scheme', 'host');
$forbidden_parts = array('user', 'pass', 'fragment');
$keys = array_keys($parts);
@ -65,9 +120,7 @@ class Auth_OpenID_TrustRoot {
return false;
}
// Return false if the original trust root value has more than
// one port specification.
if (preg_match("/:\/\/[^:]+(:\d+){2,}(\/|$)/", $trust_root)) {
if (!preg_match(Auth_OpenID___HostSegmentRe, $parts['host'])) {
return false;
}
@ -103,16 +156,21 @@ class Auth_OpenID_TrustRoot {
if (isset($parts['path'])) {
$path = strtolower($parts['path']);
if (substr($path, -1) != '/') {
$path .= '/';
if (substr($path, 0, 1) != '/') {
return false;
}
} else {
$path = '/';
}
$parts['path'] = $path;
if (!isset($parts['port'])) {
$parts['port'] = false;
}
$parts['unparsed'] = $trust_root;
return $parts;
}
@ -141,7 +199,7 @@ class Auth_OpenID_TrustRoot {
* @param string $trust_root The trust root to check
* @return bool $sanity Whether the trust root looks OK
*/
function isSane($trust_root)
static function isSane($trust_root)
{
$parts = Auth_OpenID_TrustRoot::_parse($trust_root);
if ($parts === false) {
@ -152,6 +210,25 @@ class Auth_OpenID_TrustRoot {
if ($parts['host'] == 'localhost') {
return true;
}
$host_parts = explode('.', $parts['host']);
if ($parts['wildcard']) {
// Remove the empty string from the beginning of the array
array_shift($host_parts);
}
if ($host_parts && !$host_parts[count($host_parts) - 1]) {
array_pop($host_parts);
}
if (!$host_parts) {
return false;
}
// Don't allow adjacent dots
if (in_array('', $host_parts, true)) {
return false;
}
// Get the top-level domain of the host. If it is not a valid TLD,
// it's not sane.
@ -161,19 +238,20 @@ class Auth_OpenID_TrustRoot {
}
$tld = $matches[1];
// Require at least two levels of specificity for non-country
// tlds and three levels for country tlds.
$elements = explode('.', $parts['host']);
$n = count($elements);
if ($parts['wildcard']) {
$n -= 1;
}
if (strlen($tld) == 2) {
$n -= 1;
}
if ($n <= 1) {
if (count($host_parts) == 1) {
return false;
}
if ($parts['wildcard']) {
// It's a 2-letter tld with a short second to last segment
// so there needs to be more than two segments specified
// (e.g. *.co.uk is insane)
$second_level = $host_parts[count($host_parts) - 2];
if (strlen($tld) == 2 && strlen($second_level) <= 3) {
return count($host_parts) > 2;
}
}
return true;
}
@ -191,7 +269,7 @@ class Auth_OpenID_TrustRoot {
* @return bool $matches Whether the URL matches against the
* trust root
*/
function match($trust_root, $url)
static function match($trust_root, $url)
{
$trust_root_parsed = Auth_OpenID_TrustRoot::_parse($trust_root);
$url_parsed = Auth_OpenID_TrustRoot::_parse($url);
@ -221,8 +299,14 @@ class Auth_OpenID_TrustRoot {
$base_path = $trust_root_parsed['path'];
$path = $url_parsed['path'];
if (!isset($trust_root_parsed['query'])) {
if (substr($path, 0, strlen($base_path)) != $base_path) {
return false;
if ($base_path != $path) {
if (substr($path, 0, strlen($base_path)) != $base_path) {
return false;
}
if (substr($base_path, strlen($base_path) - 1, 1) != '/' &&
substr($path, strlen($base_path), 1) != '/') {
return false;
}
}
} else {
$base_query = $trust_root_parsed['query'];
@ -240,4 +324,138 @@ class Auth_OpenID_TrustRoot {
$url_parsed['port'] === $trust_root_parsed['port']);
}
}
?>
/*
* If the endpoint is a relying party OpenID return_to endpoint,
* return the endpoint URL. Otherwise, return None.
*
* This function is intended to be used as a filter for the Yadis
* filtering interface.
*
* @see: C{L{openid.yadis.services}}
* @see: C{L{openid.yadis.filters}}
*
* @param endpoint: An XRDS BasicServiceEndpoint, as returned by
* performing Yadis dicovery.
*
* @returns: The endpoint URL or None if the endpoint is not a
* relying party endpoint.
*/
function filter_extractReturnURL($endpoint)
{
if ($endpoint->matchTypes(array(Auth_OpenID_RP_RETURN_TO_URL_TYPE))) {
return $endpoint;
} else {
return null;
}
}
function &Auth_OpenID_extractReturnURL(&$endpoint_list)
{
$result = array();
foreach ($endpoint_list as $endpoint) {
if (filter_extractReturnURL($endpoint)) {
$result[] = $endpoint;
}
}
return $result;
}
/*
* Is the return_to URL under one of the supplied allowed return_to
* URLs?
*/
function Auth_OpenID_returnToMatches($allowed_return_to_urls, $return_to)
{
foreach ($allowed_return_to_urls as $allowed_return_to) {
// A return_to pattern works the same as a realm, except that
// it's not allowed to use a wildcard. We'll model this by
// parsing it as a realm, and not trying to match it if it has
// a wildcard.
$return_realm = Auth_OpenID_TrustRoot::_parse($allowed_return_to);
if (// Parses as a trust root
($return_realm !== false) &&
// Does not have a wildcard
(!$return_realm['wildcard']) &&
// Matches the return_to that we passed in with it
(Auth_OpenID_TrustRoot::match($allowed_return_to, $return_to))) {
return true;
}
}
// No URL in the list matched
return false;
}
/*
* Given a relying party discovery URL return a list of return_to
* URLs.
*/
function Auth_OpenID_getAllowedReturnURLs($relying_party_url, $fetcher,
$discover_function=null)
{
if ($discover_function === null) {
$discover_function = array('Auth_Yadis_Yadis', 'discover');
}
$xrds_parse_cb = array('Auth_OpenID_ServiceEndpoint', 'consumerFromXRDS');
list($rp_url_after_redirects, $endpoints) =
Auth_Yadis_getServiceEndpoints($relying_party_url, $xrds_parse_cb,
$discover_function, $fetcher);
if ($rp_url_after_redirects != $relying_party_url) {
// Verification caused a redirect
return false;
}
call_user_func_array($discover_function,
array($relying_party_url, &$fetcher));
$return_to_urls = array();
$matching_endpoints = Auth_OpenID_extractReturnURL($endpoints);
foreach ($matching_endpoints as $e) {
$return_to_urls[] = $e->server_url;
}
return $return_to_urls;
}
/*
* Verify that a return_to URL is valid for the given realm.
*
* This function builds a discovery URL, performs Yadis discovery on
* it, makes sure that the URL does not redirect, parses out the
* return_to URLs, and finally checks to see if the current return_to
* URL matches the return_to.
*
* @return true if the return_to URL is valid for the realm
*/
function Auth_OpenID_verifyReturnTo($realm_str, $return_to, $fetcher,
$_vrfy='Auth_OpenID_getAllowedReturnURLs')
{
$disco_url = Auth_OpenID_TrustRoot::buildDiscoveryURL($realm_str);
if ($disco_url === false) {
return false;
}
$allowable_urls = call_user_func_array($_vrfy,
array($disco_url, $fetcher));
// The realm_str could not be parsed.
if ($allowable_urls === false) {
return false;
}
if (Auth_OpenID_returnToMatches($allowable_urls, $return_to)) {
return true;
} else {
return false;
}
}

View file

@ -5,11 +5,11 @@
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
require_once 'Services/Yadis/Misc.php';
require_once 'Auth/Yadis/Misc.php';
// from appendix B of rfc 3986 (http://www.ietf.org/rfc/rfc3986.txt)
function Auth_OpenID_getURIPattern()
@ -27,6 +27,17 @@ function Auth_OpenID_getEncodedPattern()
return '/%([0-9A-Fa-f]{2})/';
}
# gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
#
# sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
# / "*" / "+" / "," / ";" / "="
#
# unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
function Auth_OpenID_getURLIllegalCharRE()
{
return "/([^-A-Za-z0-9:\/\?#\[\]@\!\$&'\(\)\*\+,;=\._~\%])/";
}
function Auth_OpenID_getUnreserved()
{
$_unreserved = array();
@ -57,8 +68,8 @@ function Auth_OpenID_getUnreserved()
function Auth_OpenID_getEscapeRE()
{
$parts = array();
foreach (array_merge(Services_Yadis_getUCSChars(),
Services_Yadis_getIPrivateChars()) as $pair) {
foreach (array_merge(Auth_Yadis_getUCSChars(),
Auth_Yadis_getIPrivateChars()) as $pair) {
list($m, $n) = $pair;
$parts[] = sprintf("%s-%s", chr($m), chr($n));
}
@ -88,17 +99,17 @@ function Auth_OpenID_pct_encoded_replace($mo)
function Auth_OpenID_remove_dot_segments($path)
{
$result_segments = array();
while ($path) {
if (Services_Yadis_startswith($path, '../')) {
if (Auth_Yadis_startswith($path, '../')) {
$path = substr($path, 3);
} else if (Services_Yadis_startswith($path, './')) {
} else if (Auth_Yadis_startswith($path, './')) {
$path = substr($path, 2);
} else if (Services_Yadis_startswith($path, '/./')) {
} else if (Auth_Yadis_startswith($path, '/./')) {
$path = substr($path, 2);
} else if ($path == '/.') {
$path = '/';
} else if (Services_Yadis_startswith($path, '/../')) {
} else if (Auth_Yadis_startswith($path, '/../')) {
$path = substr($path, 3);
if ($result_segments) {
array_pop($result_segments);
@ -139,6 +150,13 @@ function Auth_OpenID_urinorm($uri)
}
}
$illegal_matches = array();
preg_match(Auth_OpenID_getURLIllegalCharRE(),
$uri, $illegal_matches);
if ($illegal_matches) {
return null;
}
$scheme = $uri_matches[2];
if ($scheme) {
$scheme = strtolower($scheme);
@ -228,4 +246,4 @@ function Auth_OpenID_urinorm($uri)
return $scheme . '://' . $authority . $path . $query . $fragment;
}
?>

View file

@ -0,0 +1,174 @@
<?php
/**
* This module contains the HTTP fetcher interface
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* Require logging functionality
*/
require_once "Auth/OpenID.php";
define('Auth_OpenID_FETCHER_MAX_RESPONSE_KB', 1024);
define('Auth_OpenID_USER_AGENT',
'php-openid/'.Auth_OpenID_VERSION.' (php/'.phpversion().')');
class Auth_Yadis_HTTPResponse {
function Auth_Yadis_HTTPResponse($final_url = null, $status = null,
$headers = null, $body = null)
{
$this->final_url = $final_url;
$this->status = $status;
$this->headers = $headers;
$this->body = $body;
}
}
/**
* This class is the interface for HTTP fetchers the Yadis library
* uses. This interface is only important if you need to write a new
* fetcher for some reason.
*
* @access private
* @package OpenID
*/
class Auth_Yadis_HTTPFetcher {
var $timeout = 20; // timeout in seconds.
/**
* Return whether a URL can be fetched. Returns false if the URL
* scheme is not allowed or is not supported by this fetcher
* implementation; returns true otherwise.
*
* @return bool
*/
function canFetchURL($url)
{
if ($this->isHTTPS($url) && !$this->supportsSSL()) {
Auth_OpenID::log("HTTPS URL unsupported fetching %s",
$url);
return false;
}
if (!$this->allowedURL($url)) {
Auth_OpenID::log("URL fetching not allowed for '%s'",
$url);
return false;
}
return true;
}
/**
* Return whether a URL should be allowed. Override this method to
* conform to your local policy.
*
* By default, will attempt to fetch any http or https URL.
*/
function allowedURL($url)
{
return $this->URLHasAllowedScheme($url);
}
/**
* Does this fetcher implementation (and runtime) support fetching
* HTTPS URLs? May inspect the runtime environment.
*
* @return bool $support True if this fetcher supports HTTPS
* fetching; false if not.
*/
function supportsSSL()
{
trigger_error("not implemented", E_USER_ERROR);
}
/**
* Is this an https URL?
*
* @access private
*/
function isHTTPS($url)
{
return (bool)preg_match('/^https:\/\//i', $url);
}
/**
* Is this an http or https URL?
*
* @access private
*/
function URLHasAllowedScheme($url)
{
return (bool)preg_match('/^https?:\/\//i', $url);
}
/**
* @access private
*/
function _findRedirect($headers, $url)
{
foreach ($headers as $line) {
if (strpos(strtolower($line), "location: ") === 0) {
$parts = explode(" ", $line, 2);
$loc = $parts[1];
$ppos = strpos($loc, "://");
if ($ppos === false || $ppos > strpos($loc, "/")) {
/* no host; add it */
$hpos = strpos($url, "://");
$prt = substr($url, 0, $hpos+3);
$url = substr($url, $hpos+3);
if (substr($loc, 0, 1) == "/") {
/* absolute path */
$fspos = strpos($url, "/");
if ($fspos) $loc = $prt.substr($url, 0, $fspos).$loc;
else $loc = $prt.$url.$loc;
} else {
/* relative path */
$pp = $prt;
while (1) {
$xpos = strpos($url, "/");
if ($xpos === false) break;
$apos = strpos($url, "?");
if ($apos !== false && $apos < $xpos) break;
$apos = strpos($url, "&");
if ($apos !== false && $apos < $xpos) break;
$pp .= substr($url, 0, $xpos+1);
$url = substr($url, $xpos+1);
}
$loc = $pp.$loc;
}
}
return $loc;
}
}
return null;
}
/**
* Fetches the specified URL using optional extra headers and
* returns the server's response.
*
* @param string $url The URL to be fetched.
* @param array $extra_headers An array of header strings
* (e.g. "Accept: text/html").
* @return mixed $result An array of ($code, $url, $headers,
* $body) if the URL could be fetched; null if the URL does not
* pass the URLHasAllowedScheme check or if the server's response
* is malformed.
*/
function get($url, $headers = null)
{
trigger_error("not implemented", E_USER_ERROR);
}
}

View file

@ -4,17 +4,17 @@
* Yadis service manager to be used during yadis-driven authentication
* attempts.
*
* @package Yadis
* @package OpenID
*/
/**
* The base session class used by the Services_Yadis_Manager. This
* The base session class used by the Auth_Yadis_Manager. This
* class wraps the default PHP session machinery and should be
* subclassed if your application doesn't use PHP sessioning.
*
* @package Yadis
* @package OpenID
*/
class Services_Yadis_PHPSession {
class Auth_Yadis_PHPSession {
/**
* Set a session key/value pair.
*
@ -70,10 +70,14 @@ class Services_Yadis_PHPSession {
* for dumb objects that just need to have attributes set. The idea
* is that you'll subclass this and override $this->check($data) ->
* bool to implement your own session data validation.
*
* @package OpenID
*/
class Services_Yadis_SessionLoader {
class Auth_Yadis_SessionLoader {
/**
* Override this.
*
* @access private
*/
function check($data)
{
@ -87,6 +91,8 @@ class Services_Yadis_SessionLoader {
* $this->requiredKeys(). Returns null if $this->check($data)
* evaluates to false. Returns null if $this->newObject()
* evaluates to false.
*
* @access private
*/
function fromSession($data)
{
@ -124,6 +130,8 @@ class Services_Yadis_SessionLoader {
* Prepares the data array by making any necessary changes.
* Returns an array whose keys and values will be used to update
* the original data array before calling $this->newObject($data).
*
* @access private
*/
function prepareForLoad($data)
{
@ -135,6 +143,8 @@ class Services_Yadis_SessionLoader {
* session data to construct it if necessary. The object need
* only be created; $this->fromSession() will take care of setting
* the object's attributes.
*
* @access private
*/
function newObject($data)
{
@ -146,6 +156,8 @@ class Services_Yadis_SessionLoader {
* of $obj. If $this->prepareForSave($obj) returns an array, its keys
* and values are used to update the $data array of attributes
* from $obj.
*
* @access private
*/
function toSession($obj)
{
@ -167,6 +179,8 @@ class Services_Yadis_SessionLoader {
/**
* Override this.
*
* @access private
*/
function prepareForSave($obj)
{
@ -174,7 +188,12 @@ class Services_Yadis_SessionLoader {
}
}
class Auth_OpenID_ServiceEndpointLoader extends Services_Yadis_SessionLoader {
/**
* A concrete loader implementation for Auth_OpenID_ServiceEndpoints.
*
* @package OpenID
*/
class Auth_OpenID_ServiceEndpointLoader extends Auth_Yadis_SessionLoader {
function newObject($data)
{
return new Auth_OpenID_ServiceEndpoint();
@ -196,7 +215,12 @@ class Auth_OpenID_ServiceEndpointLoader extends Services_Yadis_SessionLoader {
}
}
class Services_Yadis_ManagerLoader extends Services_Yadis_SessionLoader {
/**
* A concrete loader implementation for Auth_Yadis_Managers.
*
* @package OpenID
*/
class Auth_Yadis_ManagerLoader extends Auth_Yadis_SessionLoader {
function requiredKeys()
{
return array('starting_url',
@ -209,7 +233,7 @@ class Services_Yadis_ManagerLoader extends Services_Yadis_SessionLoader {
function newObject($data)
{
return new Services_Yadis_Manager($data['starting_url'],
return new Auth_Yadis_Manager($data['starting_url'],
$data['yadis_url'],
$data['services'],
$data['session_key']);
@ -247,16 +271,16 @@ class Services_Yadis_ManagerLoader extends Services_Yadis_SessionLoader {
* a caller attempt to use each one. This is used by the Yadis
* library internally.
*
* @package Yadis
* @package OpenID
*/
class Services_Yadis_Manager {
class Auth_Yadis_Manager {
/**
* Intialize a new yadis service manager.
*
* @access private
*/
function Services_Yadis_Manager($starting_url, $yadis_url,
function Auth_Yadis_Manager($starting_url, $yadis_url,
$services, $session_key)
{
// The URL that was used to initiate the Yadis protocol
@ -337,12 +361,12 @@ class Services_Yadis_Manager {
*
* High-level usage pattern is to call .getNextService(discover) in
* order to find the next available service for this user for this
* session. Once a request completes, call .finish() to clean up the
* session. Once a request completes, call .cleanup() to clean up the
* session state.
*
* @package Yadis
* @package OpenID
*/
class Services_Yadis_Discovery {
class Auth_Yadis_Discovery {
/**
* @access private
@ -357,17 +381,17 @@ class Services_Yadis_Discovery {
/**
* Initialize a discovery object.
*
* @param Services_Yadis_PHPSession $session An object which
* implements the Services_Yadis_PHPSession API.
* @param Auth_Yadis_PHPSession $session An object which
* implements the Auth_Yadis_PHPSession API.
* @param string $url The URL on which to attempt discovery.
* @param string $session_key_suffix The optional session key
* suffix override.
*/
function Services_Yadis_Discovery(&$session, $url,
function Auth_Yadis_Discovery($session, $url,
$session_key_suffix = null)
{
/// Initialize a discovery object
$this->session =& $session;
$this->session = $session;
$this->url = $url;
if ($session_key_suffix === null) {
$session_key_suffix = $this->DEFAULT_SUFFIX;
@ -381,21 +405,23 @@ class Services_Yadis_Discovery {
* Return the next authentication service for the pair of
* user_input and session. This function handles fallback.
*/
function getNextService($discover_cb, &$fetcher)
function getNextService($discover_cb, $fetcher)
{
$manager = $this->getManager();
if (!$manager || (!$manager->services)) {
$this->destroyManager();
$http_response = array();
$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, $this->url);
$manager = $this->createManager($services, $yadis_url);
}
if ($manager) {
$loader = new Services_Yadis_ManagerLoader();
$loader = new Auth_Yadis_ManagerLoader();
$service = $manager->nextService();
$this->session->set($this->session_key,
serialize($loader->toSession($manager)));
@ -410,13 +436,16 @@ class Services_Yadis_Discovery {
* Clean up Yadis-related services in the session and return the
* most-recently-attempted service from the manager, if one
* exists.
*
* @param $force True if the manager should be deleted regardless
* of whether it's a manager for $this->url.
*/
function cleanup()
function cleanup($force=false)
{
$manager = $this->getManager();
$manager = $this->getManager($force);
if ($manager) {
$service = $manager->current();
$this->destroyManager();
$this->destroyManager($force);
} else {
$service = null;
}
@ -435,8 +464,11 @@ class Services_Yadis_Discovery {
/**
* @access private
*
* @param $force True if the manager should be returned regardless
* of whether it's a manager for $this->url.
*/
function &getManager()
function getManager($force=false)
{
// Extract the YadisServiceManager for this object's URL and
// suffix from the session.
@ -445,22 +477,19 @@ class Services_Yadis_Discovery {
$manager = null;
if ($manager_str !== null) {
$loader = new Services_Yadis_ManagerLoader();
$loader = new Auth_Yadis_ManagerLoader();
$manager = $loader->fromSession(unserialize($manager_str));
}
if ($manager && $manager->forURL($this->url)) {
if ($manager && ($manager->forURL($this->url) || $force)) {
return $manager;
} else {
$unused = null;
return $unused;
}
}
/**
* @access private
*/
function &createManager($services, $yadis_url = null)
function createManager($services, $yadis_url = null)
{
$key = $this->getSessionKey();
if ($this->getManager()) {
@ -468,29 +497,27 @@ class Services_Yadis_Discovery {
}
if ($services) {
$loader = new Services_Yadis_ManagerLoader();
$manager = new Services_Yadis_Manager($this->url, $yadis_url,
$loader = new Auth_Yadis_ManagerLoader();
$manager = new Auth_Yadis_Manager($this->url, $yadis_url,
$services, $key);
$this->session->set($this->session_key,
serialize($loader->toSession($manager)));
return $manager;
} else {
// Oh, PHP.
$unused = null;
return $unused;
}
}
/**
* @access private
*
* @param $force True if the manager should be deleted regardless
* of whether it's a manager for $this->url.
*/
function destroyManager()
function destroyManager($force=false)
{
if ($this->getManager() !== null) {
if ($this->getManager($force) !== null) {
$key = $this->getSessionKey();
$this->session->del($key);
}
}
}
?>

View file

@ -5,11 +5,11 @@
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
function Services_Yadis_getUCSChars()
function Auth_Yadis_getUCSChars()
{
return array(
array(0xA0, 0xD7FF),
@ -32,7 +32,7 @@ function Services_Yadis_getUCSChars()
);
}
function Services_Yadis_getIPrivateChars()
function Auth_Yadis_getIPrivateChars()
{
return array(
array(0xE000, 0xF8FF),
@ -41,7 +41,7 @@ function Services_Yadis_getIPrivateChars()
);
}
function Services_Yadis_pct_escape_unicode($char_match)
function Auth_Yadis_pct_escape_unicode($char_match)
{
$c = $char_match[0];
$result = "";
@ -51,9 +51,8 @@ function Services_Yadis_pct_escape_unicode($char_match)
return $result;
}
function Services_Yadis_startswith($s, $stuff)
function Auth_Yadis_startswith($s, $stuff)
{
return strpos($s, $stuff) === 0;
}
?>

View file

@ -0,0 +1,267 @@
<?php
/**
* This module contains the CURL-based HTTP fetcher implementation.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* Interface import
*/
require_once "Auth/Yadis/HTTPFetcher.php";
require_once "Auth/OpenID.php";
/**
* A paranoid {@link Auth_Yadis_HTTPFetcher} class which uses CURL
* for fetching.
*
* @package OpenID
*/
class Auth_Yadis_ParanoidHTTPFetcher extends Auth_Yadis_HTTPFetcher {
function Auth_Yadis_ParanoidHTTPFetcher()
{
$this->reset();
}
function reset()
{
$this->headers = array();
$this->data = "";
}
/**
* @access private
*/
function _writeHeader($ch, $header)
{
array_push($this->headers, rtrim($header));
return strlen($header);
}
/**
* @access private
*/
function _writeData($ch, $data)
{
if (strlen($this->data) > 1024*Auth_OpenID_FETCHER_MAX_RESPONSE_KB) {
return 0;
} else {
$this->data .= $data;
return strlen($data);
}
}
/**
* Does this fetcher support SSL URLs?
*/
function supportsSSL()
{
$v = curl_version();
if(is_array($v)) {
return in_array('https', $v['protocols']);
} elseif (is_string($v)) {
return preg_match('/OpenSSL/i', $v);
} else {
return 0;
}
}
function get($url, $extra_headers = null)
{
if (!$this->canFetchURL($url)) {
return null;
}
$stop = time() + $this->timeout;
$off = $this->timeout;
$redir = true;
while ($redir && ($off > 0)) {
$this->reset();
$c = curl_init();
if ($c === false) {
Auth_OpenID::log(
"curl_init returned false; could not " .
"initialize for URL '%s'", $url);
return null;
}
if (defined('CURLOPT_NOSIGNAL')) {
curl_setopt($c, CURLOPT_NOSIGNAL, true);
}
if (!$this->allowedURL($url)) {
Auth_OpenID::log("Fetching URL not allowed: %s",
$url);
return null;
}
curl_setopt($c, CURLOPT_WRITEFUNCTION,
array($this, "_writeData"));
curl_setopt($c, CURLOPT_HEADERFUNCTION,
array($this, "_writeHeader"));
if ($extra_headers) {
curl_setopt($c, CURLOPT_HTTPHEADER, $extra_headers);
}
$cv = curl_version();
if(is_array($cv)) {
$curl_user_agent = 'curl/'.$cv['version'];
} else {
$curl_user_agent = $cv;
}
curl_setopt($c, CURLOPT_USERAGENT,
Auth_OpenID_USER_AGENT.' '.$curl_user_agent);
curl_setopt($c, CURLOPT_TIMEOUT, $off);
curl_setopt($c, CURLOPT_URL, $url);
if (defined('Auth_OpenID_VERIFY_HOST')) {
// 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);
$code = curl_getinfo($c, CURLINFO_HTTP_CODE);
$body = $this->data;
$headers = $this->headers;
if (!$code) {
Auth_OpenID::log("Got no response code when fetching %s", $url);
Auth_OpenID::log("CURL error (%s): %s",
curl_errno($c), curl_error($c));
return null;
}
if (in_array($code, array(301, 302, 303, 307))) {
$url = $this->_findRedirect($headers, $url);
$redir = true;
} else {
$redir = false;
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);
}
$new_headers = array();
foreach ($headers as $header) {
if (strpos($header, ': ')) {
list($name, $value) = explode(': ', $header, 2);
$new_headers[$name] = $value;
}
}
Auth_OpenID::log(
"Successfully fetched '%s': GET response code %s",
$url, $code);
return new Auth_Yadis_HTTPResponse($url, $code,
$new_headers, $body);
}
$off = $stop - time();
}
return null;
}
function post($url, $body, $extra_headers = null)
{
if (!$this->canFetchURL($url)) {
return null;
}
$this->reset();
$c = curl_init();
if (defined('CURLOPT_NOSIGNAL')) {
curl_setopt($c, CURLOPT_NOSIGNAL, true);
}
curl_setopt($c, CURLOPT_POST, true);
curl_setopt($c, CURLOPT_POSTFIELDS, $body);
curl_setopt($c, CURLOPT_TIMEOUT, $this->timeout);
curl_setopt($c, CURLOPT_URL, $url);
curl_setopt($c, CURLOPT_WRITEFUNCTION,
array($this, "_writeData"));
if (defined('Auth_OpenID_VERIFY_HOST')) {
// 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);
$code = curl_getinfo($c, CURLINFO_HTTP_CODE);
if (!$code) {
Auth_OpenID::log("Got no response code when fetching %s", $url);
Auth_OpenID::log("CURL error (%s): %s",
curl_errno($c), curl_error($c));
return null;
}
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);
}
$body = $this->data;
curl_close($c);
$new_headers = $extra_headers;
foreach ($this->headers as $header) {
if (strpos($header, ': ')) {
list($name, $value) = explode(': ', $header, 2);
$new_headers[$name] = $value;
}
}
Auth_OpenID::log("Successfully fetched '%s': POST response code %s",
$url, $code);
return new Auth_Yadis_HTTPResponse($url, $code,
$new_headers, $body);
}
}

View file

@ -7,10 +7,10 @@
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package Yadis
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
@ -18,15 +18,21 @@
* tags and their attributes. This is used by the Yadis discovery
* process. This class must be instantiated to be used.
*
* @package Yadis
* @package OpenID
*/
class Services_Yadis_ParseHTML {
class Auth_Yadis_ParseHTML {
/**
* @access private
*/
var $_re_flags = "si";
/**
* @access private
*/
var $_removed_re =
"<!--.*?-->|<!\[CDATA\[.*?\]\]>|<script\b(?!:)[^>]*>.*?<\/script>";
/**
* @access private
*/
@ -35,14 +41,18 @@ class Services_Yadis_ParseHTML {
/**
* @access private
*/
var $_attr_find = '\b([-\w]+)=(".*?"|\'.*?\'|.+?)[\s>]';
var $_attr_find = '\b([-\w]+)=(".*?"|\'.*?\'|.+?)[\/\s>]';
function Services_Yadis_ParseHTML()
function Auth_Yadis_ParseHTML()
{
$this->_attr_find = sprintf("/%s/%s",
$this->_attr_find,
$this->_re_flags);
$this->_removed_re = sprintf("/%s/%s",
$this->_removed_re,
$this->_re_flags);
$this->_entity_replacements = array(
'amp' => '&',
'lt' => '<',
@ -146,6 +156,10 @@ class Services_Yadis_ParseHTML {
*/
function getMetaTags($html_string)
{
$html_string = preg_replace($this->_removed_re,
"",
$html_string);
$key_tags = array($this->tagPattern('html', false, false),
$this->tagPattern('head', false, false),
$this->tagPattern('head', true, false),
@ -185,7 +199,8 @@ class Services_Yadis_ParseHTML {
if (!is_null($key_tags_pos[0]) && $key_tags_pos[1] < $key_tags_pos[0]) {
return array();
}
$html_string = substr($html_string, $key_tags_pos[1], ($key_tags_pos[2]-$key_tags_pos[1]));
$html_string = substr($html_string, $key_tags_pos[1],
($key_tags_pos[2]-$key_tags_pos[1]));
$link_data = array();
$link_matches = array();
@ -241,4 +256,3 @@ class Services_Yadis_ParseHTML {
}
}
?>

View file

@ -8,29 +8,35 @@
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package Yadis
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* Interface import
*/
require_once "Services/Yadis/HTTPFetcher.php";
require_once "Auth/Yadis/HTTPFetcher.php";
/**
* This class implements a plain, hand-built socket-based fetcher
* which will be used in the event that CURL is unavailable.
*
* @package Yadis
* @package OpenID
*/
class Services_Yadis_PlainHTTPFetcher extends Services_Yadis_HTTPFetcher {
class Auth_Yadis_PlainHTTPFetcher extends Auth_Yadis_HTTPFetcher {
/**
* Does this fetcher support SSL URLs?
*/
function supportsSSL()
{
return function_exists('openssl_open');
}
function get($url, $extra_headers = null)
{
if (!$this->allowedURL($url)) {
trigger_error("Bad URL scheme in url: " . $url,
E_USER_WARNING);
if (!$this->canFetchURL($url)) {
return null;
}
@ -53,21 +59,21 @@ class Services_Yadis_PlainHTTPFetcher extends Services_Yadis_HTTPFetcher {
} elseif ($parts['scheme'] == 'https') {
$parts['port'] = 443;
} else {
trigger_error("fetcher post method doesn't support " .
" scheme '" . $parts['scheme'] .
"', no default port available",
E_USER_WARNING);
return null;
}
}
if (!array_key_exists('path', $parts)) {
$parts['path'] = '/';
}
$host = $parts['host'];
if ($parts['scheme'] == 'https') {
$host = 'ssl://' . $host;
}
$user_agent = "PHP Yadis Library Fetcher";
$user_agent = Auth_OpenID_USER_AGENT;
$headers = array(
"GET ".$parts['path'].
@ -99,8 +105,11 @@ class Services_Yadis_PlainHTTPFetcher extends Services_Yadis_HTTPFetcher {
fputs($sock, implode("\r\n", $headers) . "\r\n\r\n");
$data = "";
while (!feof($sock)) {
$kilobytes = 0;
while (!feof($sock) &&
$kilobytes < Auth_OpenID_FETCHER_MAX_RESPONSE_KB ) {
$data .= fgets($sock, 1024);
$kilobytes += 1;
}
fclose($sock);
@ -113,7 +122,7 @@ class Services_Yadis_PlainHTTPFetcher extends Services_Yadis_HTTPFetcher {
$code = $http_code[1];
if (in_array($code, array('301', '302'))) {
$url = $this->_findRedirect($headers);
$url = $this->_findRedirect($headers, $url);
$redir = true;
} else {
$redir = false;
@ -126,20 +135,22 @@ class Services_Yadis_PlainHTTPFetcher extends Services_Yadis_HTTPFetcher {
foreach ($headers as $header) {
if (preg_match("/:/", $header)) {
list($name, $value) = explode(": ", $header, 2);
$new_headers[$name] = $value;
$parts = explode(": ", $header, 2);
if (count($parts) == 2) {
list($name, $value) = $parts;
$new_headers[$name] = $value;
}
}
}
return new Services_Yadis_HTTPResponse($url, $code, $new_headers, $body);
return new Auth_Yadis_HTTPResponse($url, $code, $new_headers, $body);
}
function post($url, $body, $extra_headers = null)
{
if (!$this->allowedURL($url)) {
trigger_error("Bad URL scheme in url: " . $url,
E_USER_WARNING);
if (!$this->canFetchURL($url)) {
return null;
}
@ -175,10 +186,6 @@ class Services_Yadis_PlainHTTPFetcher extends Services_Yadis_HTTPFetcher {
} elseif ($parts['scheme'] == 'https') {
$parts['port'] = 443;
} else {
trigger_error("fetcher post method doesn't support scheme '" .
$parts['scheme'] .
"', no default port available",
E_USER_WARNING);
return null;
}
}
@ -195,9 +202,6 @@ class Services_Yadis_PlainHTTPFetcher extends Services_Yadis_HTTPFetcher {
$this->timeout);
if ($sock === false) {
trigger_error("Could not connect to " . $parts['host'] .
" port " . $parts['port'],
E_USER_WARNING);
return null;
}
@ -237,9 +241,8 @@ class Services_Yadis_PlainHTTPFetcher extends Services_Yadis_HTTPFetcher {
}
return new Services_Yadis_HTTPResponse($url, $code,
$headers, $response_body);
return new Auth_Yadis_HTTPResponse($url, $code,
$new_headers, $response_body);
}
}
?>

View file

@ -4,23 +4,23 @@
* XML-parsing classes to wrap the domxml and DOM extensions for PHP 4
* and 5, respectively.
*
* @package Yadis
* @package OpenID
*/
/**
* The base class for wrappers for available PHP XML-parsing
* extensions. To work with this Yadis library, subclasses of this
* class MUST implement the API as defined in the remarks for this
* class. Subclasses of Services_Yadis_XMLParser are used to wrap
* class. Subclasses of Auth_Yadis_XMLParser are used to wrap
* particular PHP XML extensions such as 'domxml'. These are used
* internally by the library depending on the availability of
* supported PHP XML extensions.
*
* @package Yadis
* @package OpenID
*/
class Services_Yadis_XMLParser {
class Auth_Yadis_XMLParser {
/**
* Initialize an instance of Services_Yadis_XMLParser with some
* Initialize an instance of Auth_Yadis_XMLParser with some
* XML and namespaces. This SHOULD NOT be overridden by
* subclasses.
*
@ -91,7 +91,7 @@ class Services_Yadis_XMLParser {
* @return array $node_list An array of matching opaque node
* objects to be used with other methods of this parser class.
*/
function evalXPath($xpath, $node = null)
function &evalXPath($xpath, $node = null)
{
// Not implemented.
}
@ -125,16 +125,16 @@ class Services_Yadis_XMLParser {
}
/**
* This concrete implementation of Services_Yadis_XMLParser implements
* This concrete implementation of Auth_Yadis_XMLParser implements
* the appropriate API for the 'domxml' extension which is typically
* packaged with PHP 4. This class will be used whenever the 'domxml'
* extension is detected. See the Services_Yadis_XMLParser class for
* extension is detected. See the Auth_Yadis_XMLParser class for
* details on this class's methods.
*
* @package Yadis
* @package OpenID
*/
class Services_Yadis_domxml extends Services_Yadis_XMLParser {
function Services_Yadis_domxml()
class Auth_Yadis_domxml extends Auth_Yadis_XMLParser {
function Auth_Yadis_domxml()
{
$this->xml = null;
$this->doc = null;
@ -170,6 +170,11 @@ class Services_Yadis_domxml extends Services_Yadis_XMLParser {
$result = @$this->xpath->xpath_eval($xpath);
}
if (!$result) {
$n = array();
return $n;
}
if (!$result->nodeset) {
$n = array();
return $n;
@ -203,16 +208,16 @@ class Services_Yadis_domxml extends Services_Yadis_XMLParser {
}
/**
* This concrete implementation of Services_Yadis_XMLParser implements
* This concrete implementation of Auth_Yadis_XMLParser implements
* the appropriate API for the 'dom' extension which is typically
* packaged with PHP 5. This class will be used whenever the 'dom'
* extension is detected. See the Services_Yadis_XMLParser class for
* extension is detected. See the Auth_Yadis_XMLParser class for
* details on this class's methods.
*
* @package Yadis
* @package OpenID
*/
class Services_Yadis_dom extends Services_Yadis_XMLParser {
function Services_Yadis_dom()
class Auth_Yadis_dom extends Auth_Yadis_XMLParser {
function Auth_Yadis_dom()
{
$this->xml = null;
$this->doc = null;
@ -257,6 +262,10 @@ class Services_Yadis_dom extends Services_Yadis_XMLParser {
$n = array();
if (!$result) {
return $n;
}
for ($i = 0; $i < $result->length; $i++) {
$n[] = $result->item($i);
}
@ -289,8 +298,8 @@ class Services_Yadis_dom extends Services_Yadis_XMLParser {
}
}
global $__Services_Yadis_defaultParser;
$__Services_Yadis_defaultParser = null;
global $__Auth_Yadis_defaultParser;
$__Auth_Yadis_defaultParser = null;
/**
* Set a default parser to override the extension-driven selection of
@ -298,68 +307,46 @@ $__Services_Yadis_defaultParser = null;
* one in which multiple parsers can be used but one is more
* desirable.
*
* @param Services_Yadis_XMLParser $parser An instance of a
* Services_Yadis_XMLParser subclass.
* @param Auth_Yadis_XMLParser $parser An instance of a
* Auth_Yadis_XMLParser subclass.
*/
function Services_Yadis_setDefaultParser(&$parser)
function Auth_Yadis_setDefaultParser($parser)
{
global $__Services_Yadis_defaultParser;
$__Services_Yadis_defaultParser =& $parser;
global $__Auth_Yadis_defaultParser;
$__Auth_Yadis_defaultParser = $parser;
}
function Services_Yadis_getSupportedExtensions()
function Auth_Yadis_getSupportedExtensions()
{
return array(
'dom' => array('classname' => 'Services_Yadis_dom',
'libname' => array('dom.so', 'dom.dll')),
'domxml' => array('classname' => 'Services_Yadis_domxml',
'libname' => array('domxml.so', 'php_domxml.dll')),
);
return array('dom' => 'Auth_Yadis_dom',
'domxml' => 'Auth_Yadis_domxml');
}
/**
* Returns an instance of a Services_Yadis_XMLParser subclass based on
* Returns an instance of a Auth_Yadis_XMLParser subclass based on
* the availability of PHP extensions for XML parsing. If
* Services_Yadis_setDefaultParser has been called, the parser used in
* Auth_Yadis_setDefaultParser has been called, the parser used in
* that call will be returned instead.
*/
function &Services_Yadis_getXMLParser()
function Auth_Yadis_getXMLParser()
{
global $__Services_Yadis_defaultParser;
if (isset($__Services_Yadis_defaultParser)) {
return $__Services_Yadis_defaultParser;
global $__Auth_Yadis_defaultParser;
if (isset($__Auth_Yadis_defaultParser)) {
return $__Auth_Yadis_defaultParser;
}
$p = null;
$classname = null;
$extensions = Services_Yadis_getSupportedExtensions();
// Return a wrapper for the resident implementation, if any.
foreach ($extensions as $name => $params) {
if (!extension_loaded($name)) {
foreach ($params['libname'] as $libname) {
if (@dl($libname)) {
$classname = $params['classname'];
}
}
} else {
$classname = $params['classname'];
}
if (isset($classname)) {
$p = new $classname();
return $p;
}
foreach(Auth_Yadis_getSupportedExtensions() as $extension => $classname)
{
if (extension_loaded($extension))
{
$p = new $classname();
Auth_Yadis_setDefaultParser($p);
return $p;
}
}
if (!isset($p)) {
trigger_error('No XML parser was found', E_USER_ERROR);
} else {
Services_Yadis_setDefaultParser($p);
}
return $p;
return false;
}
?>

View file

@ -7,26 +7,26 @@
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package Yadis
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* Require the XPath implementation.
*/
require_once 'Services/Yadis/XML.php';
require_once 'Auth/Yadis/XML.php';
/**
* This match mode means a given service must match ALL filters passed
* to the Services_Yadis_XRDS::services() call.
* to the Auth_Yadis_XRDS::services() call.
*/
define('SERVICES_YADIS_MATCH_ALL', 101);
/**
* This match mode means a given service must match ANY filters (at
* least one) passed to the Services_Yadis_XRDS::services() call.
* least one) passed to the Auth_Yadis_XRDS::services() call.
*/
define('SERVICES_YADIS_MATCH_ANY', 102);
@ -36,16 +36,26 @@ define('SERVICES_YADIS_MATCH_ANY', 102);
*/
define('SERVICES_YADIS_MAX_PRIORITY', pow(2, 30));
function Services_Yadis_getNSMap()
/**
* XRD XML namespace
*/
define('Auth_Yadis_XMLNS_XRD_2_0', 'xri://$xrd*($v*2.0)');
/**
* XRDS XML namespace
*/
define('Auth_Yadis_XMLNS_XRDS', 'xri://$xrds');
function Auth_Yadis_getNSMap()
{
return array('xrds' => 'xri://$xrds',
'xrd' => 'xri://$xrd*($v*2.0)');
return array('xrds' => Auth_Yadis_XMLNS_XRDS,
'xrd' => Auth_Yadis_XMLNS_XRD_2_0);
}
/**
* @access private
*/
function Services_Yadis_array_scramble($arr)
function Auth_Yadis_array_scramble($arr)
{
$result = array();
@ -61,21 +71,21 @@ function Services_Yadis_array_scramble($arr)
/**
* This class represents a <Service> element in an XRDS document.
* Objects of this type are returned by
* Services_Yadis_XRDS::services() and
* Services_Yadis_Yadis::services(). Each object corresponds directly
* Auth_Yadis_XRDS::services() and
* Auth_Yadis_Yadis::services(). Each object corresponds directly
* to a <Service> element in the XRDS and supplies a
* getElements($name) method which you should use to inspect the
* element's contents. See {@link Services_Yadis_Yadis} for more
* element's contents. See {@link Auth_Yadis_Yadis} for more
* information on the role this class plays in Yadis discovery.
*
* @package Yadis
* @package OpenID
*/
class Services_Yadis_Service {
class Auth_Yadis_Service {
/**
* Creates an empty service object.
*/
function Services_Yadis_Service()
function Auth_Yadis_Service()
{
$this->element = null;
$this->parser = null;
@ -99,6 +109,19 @@ class Services_Yadis_Service {
return $t;
}
function matchTypes($type_uris)
{
$result = array();
foreach ($this->getTypes() as $typ) {
if (in_array($typ, $type_uris)) {
$result[] = $typ;
}
}
return $result;
}
/**
* Return the URIs in the "URI" elements, if any, of this Service
* element. The URIs are returned sorted in priority order.
@ -132,12 +155,12 @@ class Services_Yadis_Service {
// Rebuild array of URIs.
$result = array();
foreach ($keys as $k) {
$new_uris = Services_Yadis_array_scramble($uris[$k]);
$new_uris = Auth_Yadis_array_scramble($uris[$k]);
$result = array_merge($result, $new_uris);
}
$result = array_merge($result,
Services_Yadis_array_scramble($last));
Auth_Yadis_array_scramble($last));
return $result;
}
@ -173,7 +196,7 @@ class Services_Yadis_Service {
* @return array $list An array of elements with the specified
* name which are direct children of the <Service> element. The
* nodes returned by this function can be passed to $this->parser
* methods (see {@link Services_Yadis_XMLParser}).
* methods (see {@link Auth_Yadis_XMLParser}).
*/
function getElements($name)
{
@ -181,46 +204,76 @@ class Services_Yadis_Service {
}
}
/*
* Return the expiration date of this XRD element, or None if no
* expiration was specified.
*
* @param $default The value to use as the expiration if no expiration
* was specified in the XRD.
*/
function Auth_Yadis_getXRDExpiration($xrd_element, $default=null)
{
$expires_element = $xrd_element->$parser->evalXPath('/xrd:Expires');
if ($expires_element === null) {
return $default;
} else {
$expires_string = $expires_element->text;
// Will raise ValueError if the string is not the expected
// format
$t = strptime($expires_string, "%Y-%m-%dT%H:%M:%SZ");
if ($t === false) {
return false;
}
// [int $hour [, int $minute [, int $second [,
// int $month [, int $day [, int $year ]]]]]]
return mktime($t['tm_hour'], $t['tm_min'], $t['tm_sec'],
$t['tm_mon'], $t['tm_day'], $t['tm_year']);
}
}
/**
* This class performs parsing of XRDS documents.
*
* You should not instantiate this class directly; rather, call
* parseXRDS statically:
*
* <pre> $xrds = Services_Yadis_XRDS::parseXRDS($xml_string);</pre>
* <pre> $xrds = Auth_Yadis_XRDS::parseXRDS($xml_string);</pre>
*
* If the XRDS can be parsed and is valid, an instance of
* Services_Yadis_XRDS will be returned. Otherwise, null will be
* returned. This class is used by the Services_Yadis_Yadis::discover
* Auth_Yadis_XRDS will be returned. Otherwise, null will be
* returned. This class is used by the Auth_Yadis_Yadis::discover
* method.
*
* @package Yadis
* @package OpenID
*/
class Services_Yadis_XRDS {
class Auth_Yadis_XRDS {
/**
* Instantiate a Services_Yadis_XRDS object. Requires an XPath
* Instantiate a Auth_Yadis_XRDS object. Requires an XPath
* instance which has been used to parse a valid XRDS document.
*/
function Services_Yadis_XRDS(&$xmlParser, &$xrdNodes)
function Auth_Yadis_XRDS($xmlParser, $xrdNodes)
{
$this->parser =& $xmlParser;
$this->parser = $xmlParser;
$this->xrdNode = $xrdNodes[count($xrdNodes) - 1];
$this->allXrdNodes =& $xrdNodes;
$this->allXrdNodes = $xrdNodes;
$this->serviceList = array();
$this->_parse();
}
/**
* Parse an XML string (XRDS document) and return either a
* Services_Yadis_XRDS object or null, depending on whether the
* Auth_Yadis_XRDS object or null, depending on whether the
* XRDS XML is valid.
*
* @param string $xml_string An XRDS XML string.
* @return mixed $xrds An instance of Services_Yadis_XRDS or null,
* @return mixed $xrds An instance of Auth_Yadis_XRDS or null,
* depending on the validity of $xml_string
*/
function &parseXRDS($xml_string, $extra_ns_map = null)
static function parseXRDS($xml_string, $extra_ns_map = null)
{
$_null = null;
@ -228,9 +281,9 @@ class Services_Yadis_XRDS {
return $_null;
}
$parser = Services_Yadis_getXMLParser();
$parser = Auth_Yadis_getXMLParser();
$ns_map = Services_Yadis_getNSMap();
$ns_map = Auth_Yadis_getNSMap();
if ($extra_ns_map && is_array($extra_ns_map)) {
$ns_map = array_merge($ns_map, $extra_ns_map);
@ -253,11 +306,11 @@ class Services_Yadis_XRDS {
$attrs = $parser->attributes($root);
if (array_key_exists('xmlns:xrd', $attrs) &&
$attrs['xmlns:xrd'] != 'xri://$xrd*($v*2.0)') {
$attrs['xmlns:xrd'] != Auth_Yadis_XMLNS_XRDS) {
return $_null;
} else if (array_key_exists('xmlns', $attrs) &&
preg_match('/xri/', $attrs['xmlns']) &&
$attrs['xmlns'] != 'xri://$xrd*($v*2.0)') {
$attrs['xmlns'] != Auth_Yadis_XMLNS_XRD_2_0) {
return $_null;
}
@ -268,7 +321,7 @@ class Services_Yadis_XRDS {
return $_null;
}
$xrds = new Services_Yadis_XRDS($parser, $xrd_nodes);
$xrds = new Auth_Yadis_XRDS($parser, $xrd_nodes);
return $xrds;
}
@ -299,9 +352,9 @@ class Services_Yadis_XRDS {
$services = $this->parser->evalXPath('xrd:Service', $this->xrdNode);
foreach ($services as $node) {
$s = new Services_Yadis_Service();
$s = new Auth_Yadis_Service();
$s->element = $node;
$s->parser =& $this->parser;
$s->parser = $this->parser;
$priority = $s->getPriority();
@ -323,7 +376,7 @@ class Services_Yadis_XRDS {
* specified filters, but $filter_mode may be
* SERVICES_YADIS_MATCH_ALL if you want to be sure that the
* returned services match all the given filters. See {@link
* Services_Yadis_Yadis} for detailed usage information on filter
* Auth_Yadis_Yadis} for detailed usage information on filter
* functions.
*
* @param mixed $filters An array of callbacks to filter the
@ -333,7 +386,7 @@ class Services_Yadis_XRDS {
* services should match ALL or ANY of the specified filters,
* respectively.
* @return mixed $services An array of {@link
* Services_Yadis_Service} objects if $filter_mode is a valid
* Auth_Yadis_Service} objects if $filter_mode is a valid
* mode; null if $filter_mode is an invalid mode (i.e., not
* SERVICES_YADIS_MATCH_ANY or SERVICES_YADIS_MATCH_ALL).
*/
@ -375,7 +428,8 @@ class Services_Yadis_XRDS {
$matches = 0;
foreach ($filters as $filter) {
if (call_user_func_array($filter, array($service))) {
if (call_user_func_array($filter, array(&$service))) {
$matches++;
if ($filter_mode == SERVICES_YADIS_MATCH_ANY) {
@ -422,4 +476,3 @@ class Services_Yadis_XRDS {
}
}
?>

View file

@ -3,31 +3,31 @@
/**
* Routines for XRI resolution.
*
* @package Yadis
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
require_once 'Services/Yadis/Misc.php';
require_once 'Services/Yadis/Yadis.php';
require_once 'Auth/Yadis/Misc.php';
require_once 'Auth/Yadis/Yadis.php';
require_once 'Auth/OpenID.php';
function Services_Yadis_getDefaultProxy()
function Auth_Yadis_getDefaultProxy()
{
return 'http://proxy.xri.net/';
return 'http://xri.net/';
}
function Services_Yadis_getXRIAuthorities()
function Auth_Yadis_getXRIAuthorities()
{
return array('!', '=', '@', '+', '$', '(');
}
function Services_Yadis_getEscapeRE()
function Auth_Yadis_getEscapeRE()
{
$parts = array();
foreach (array_merge(Services_Yadis_getUCSChars(),
Services_Yadis_getIPrivateChars()) as $pair) {
foreach (array_merge(Auth_Yadis_getUCSChars(),
Auth_Yadis_getIPrivateChars()) as $pair) {
list($m, $n) = $pair;
$parts[] = sprintf("%s-%s", chr($m), chr($n));
}
@ -35,28 +35,29 @@ function Services_Yadis_getEscapeRE()
return sprintf('/[%s]/', implode('', $parts));
}
function Services_Yadis_getXrefRE()
function Auth_Yadis_getXrefRE()
{
return '/\((.*?)\)/';
}
function Services_Yadis_identifierScheme($identifier)
function Auth_Yadis_identifierScheme($identifier)
{
if (Services_Yadis_startswith($identifier, 'xri://') ||
(in_array($identifier[0], Services_Yadis_getXRIAuthorities()))) {
if (Auth_Yadis_startswith($identifier, 'xri://') ||
($identifier &&
in_array($identifier[0], Auth_Yadis_getXRIAuthorities()))) {
return "XRI";
} else {
return "URI";
}
}
function Services_Yadis_toIRINormal($xri)
function Auth_Yadis_toIRINormal($xri)
{
if (!Services_Yadis_startswith($xri, 'xri://')) {
if (!Auth_Yadis_startswith($xri, 'xri://')) {
$xri = 'xri://' . $xri;
}
return Services_Yadis_escapeForIRI($xri);
return Auth_Yadis_escapeForIRI($xri);
}
function _escape_xref($xref_match)
@ -68,32 +69,32 @@ function _escape_xref($xref_match)
return $xref;
}
function Services_Yadis_escapeForIRI($xri)
function Auth_Yadis_escapeForIRI($xri)
{
$xri = str_replace('%', '%25', $xri);
$xri = preg_replace_callback(Services_Yadis_getXrefRE(),
$xri = preg_replace_callback(Auth_Yadis_getXrefRE(),
'_escape_xref', $xri);
return $xri;
}
function Services_Yadis_toURINormal($xri)
function Auth_Yadis_toURINormal($xri)
{
return Services_Yadis_iriToURI(Services_Yadis_toIRINormal($xri));
return Auth_Yadis_iriToURI(Auth_Yadis_toIRINormal($xri));
}
function Services_Yadis_iriToURI($iri)
function Auth_Yadis_iriToURI($iri)
{
if (1) {
return $iri;
} else {
// According to RFC 3987, section 3.1, "Mapping of IRIs to URIs"
return preg_replace_callback(Services_Yadis_getEscapeRE(),
'Services_Yadis_pct_escape_unicode', $iri);
return preg_replace_callback(Auth_Yadis_getEscapeRE(),
'Auth_Yadis_pct_escape_unicode', $iri);
}
}
function Services_Yadis_XRIAppendArgs($url, $args)
function Auth_Yadis_XRIAppendArgs($url, $args)
{
// Append some arguments to an HTTP query. Yes, this is just like
// OpenID's appendArgs, but with special seasoning for XRI
@ -133,20 +134,20 @@ function Services_Yadis_XRIAppendArgs($url, $args)
return $url . $sep . Auth_OpenID::httpBuildQuery($args);
}
function Services_Yadis_providerIsAuthoritative($providerID, $canonicalID)
function Auth_Yadis_providerIsAuthoritative($providerID, $canonicalID)
{
$lastbang = strrpos($canonicalID, '!');
$p = substr($canonicalID, 0, $lastbang);
return $p == $providerID;
}
function Services_Yadis_rootAuthority($xri)
function Auth_Yadis_rootAuthority($xri)
{
// Return the root authority for an XRI.
$root = null;
if (Services_Yadis_startswith($xri, 'xri://')) {
if (Auth_Yadis_startswith($xri, 'xri://')) {
$xri = substr($xri, 6);
}
@ -159,7 +160,7 @@ function Services_Yadis_rootAuthority($xri)
// does that before we have a real xriparse function.
// Hopefully nobody does that *ever*.
$root = substr($authority, 0, strpos($authority, ')') + 1);
} else if (in_array($authority[0], Services_Yadis_getXRIAuthorities())) {
} else if (in_array($authority[0], Auth_Yadis_getXRIAuthorities())) {
// Other XRI reference.
$root = $authority[0];
} else {
@ -172,24 +173,24 @@ function Services_Yadis_rootAuthority($xri)
$root = $segments[0];
}
return Services_Yadis_XRI($root);
return Auth_Yadis_XRI($root);
}
function Services_Yadis_XRI($xri)
function Auth_Yadis_XRI($xri)
{
if (!Services_Yadis_startswith($xri, 'xri://')) {
if (!Auth_Yadis_startswith($xri, 'xri://')) {
$xri = 'xri://' . $xri;
}
return $xri;
}
function Services_Yadis_getCanonicalID($iname, $xrds)
function Auth_Yadis_getCanonicalID($iname, $xrds)
{
// Returns FALSE or a canonical ID value.
// Returns false or a canonical ID value.
// Now nodes are in reverse order.
$xrd_list = array_reverse($xrds->allXrdNodes);
$parser =& $xrds->parser;
$parser = $xrds->parser;
$node = $xrd_list[0];
$canonicalID_nodes = $parser->evalXPath('xrd:CanonicalID', $node);
@ -198,8 +199,8 @@ function Services_Yadis_getCanonicalID($iname, $xrds)
return false;
}
$canonicalID = $canonicalID_nodes[count($canonicalID_nodes) - 1];
$canonicalID = Services_Yadis_XRI($parser->content($canonicalID));
$canonicalID = $canonicalID_nodes[0];
$canonicalID = Auth_Yadis_XRI($parser->content($canonicalID));
$childID = $canonicalID;
@ -207,13 +208,13 @@ function Services_Yadis_getCanonicalID($iname, $xrds)
$xrd = $xrd_list[$i];
$parent_sought = substr($childID, 0, strrpos($childID, '!'));
$parent_list = array();
foreach ($parser->evalXPath('xrd:CanonicalID', $xrd) as $c) {
$parent_list[] = Services_Yadis_XRI($parser->content($c));
$parentCID = $parser->evalXPath('xrd:CanonicalID', $xrd);
if (!$parentCID) {
return false;
}
$parentCID = Auth_Yadis_XRI($parser->content($parentCID[0]));
if (!in_array($parent_sought, $parent_list)) {
if (strcasecmp($parent_sought, $parentCID)) {
// raise XRDSFraud.
return false;
}
@ -221,8 +222,8 @@ function Services_Yadis_getCanonicalID($iname, $xrds)
$childID = $parent_sought;
}
$root = Services_Yadis_rootAuthority($iname);
if (!Services_Yadis_providerIsAuthoritative($root, $childID)) {
$root = Auth_Yadis_rootAuthority($iname);
if (!Auth_Yadis_providerIsAuthoritative($root, $childID)) {
// raise XRDSFraud.
return false;
}
@ -230,4 +231,4 @@ function Services_Yadis_getCanonicalID($iname, $xrds)
return $canonicalID;
}
?>

View file

@ -1,22 +1,26 @@
<?php
require_once 'Services/Yadis/XRDS.php';
require_once 'Services/Yadis/XRI.php';
/**
* Code for using a proxy XRI resolver.
*/
class Services_Yadis_ProxyResolver {
function Services_Yadis_ProxyResolver(&$fetcher, $proxy_url = null)
require_once 'Auth/Yadis/XRDS.php';
require_once 'Auth/Yadis/XRI.php';
class Auth_Yadis_ProxyResolver {
function Auth_Yadis_ProxyResolver($fetcher, $proxy_url = null)
{
$this->fetcher =& $fetcher;
$this->fetcher = $fetcher;
$this->proxy_url = $proxy_url;
if (!$this->proxy_url) {
$this->proxy_url = Services_Yadis_getDefaultProxy();
$this->proxy_url = Auth_Yadis_getDefaultProxy();
}
}
function queryURL($xri, $service_type = null)
{
// trim off the xri:// prefix
$qxri = substr(Services_Yadis_toURINormal($xri), 6);
$qxri = substr(Auth_Yadis_toURINormal($xri), 6);
$hxri = $this->proxy_url . $qxri;
$args = array(
'_xrd_r' => 'application/xrds+xml'
@ -29,7 +33,7 @@ class Services_Yadis_ProxyResolver {
$args['_xrd_r'] .= ';sep=false';
}
$query = Services_Yadis_XRIAppendArgs($hxri, $args);
$query = Auth_Yadis_XRIAppendArgs($hxri, $args);
return $query;
}
@ -40,14 +44,14 @@ class Services_Yadis_ProxyResolver {
foreach ($service_types as $service_type) {
$url = $this->queryURL($xri, $service_type);
$response = $this->fetcher->get($url);
if ($response->status != 200) {
if ($response->status != 200 and $response->status != 206) {
continue;
}
$xrds = Services_Yadis_XRDS::parseXRDS($response->body);
$xrds = Auth_Yadis_XRDS::parseXRDS($response->body);
if (!$xrds) {
continue;
}
$canonicalID = Services_Yadis_getCanonicalID($xri,
$canonicalID = Auth_Yadis_getCanonicalID($xri,
$xrds);
if ($canonicalID === false) {
@ -65,4 +69,4 @@ class Services_Yadis_ProxyResolver {
}
}
?>

View file

@ -0,0 +1,382 @@
<?php
/**
* The core PHP Yadis implementation.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package OpenID
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005-2008 Janrain, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache
*/
/**
* Need both fetcher types so we can use the right one based on the
* presence or absence of CURL.
*/
require_once "Auth/Yadis/PlainHTTPFetcher.php";
require_once "Auth/Yadis/ParanoidHTTPFetcher.php";
/**
* Need this for parsing HTML (looking for META tags).
*/
require_once "Auth/Yadis/ParseHTML.php";
/**
* Need this to parse the XRDS document during Yadis discovery.
*/
require_once "Auth/Yadis/XRDS.php";
/**
* XRDS (yadis) content type
*/
define('Auth_Yadis_CONTENT_TYPE', 'application/xrds+xml');
/**
* Yadis header
*/
define('Auth_Yadis_HEADER_NAME', 'X-XRDS-Location');
/**
* Contains the result of performing Yadis discovery on a URI.
*
* @package OpenID
*/
class Auth_Yadis_DiscoveryResult {
// The URI that was passed to the fetcher
var $request_uri = null;
// The result of following redirects from the request_uri
var $normalized_uri = null;
// The URI from which the response text was returned (set to
// None if there was no XRDS document found)
var $xrds_uri = null;
var $xrds = null;
// The content-type returned with the response_text
var $content_type = null;
// The document returned from the xrds_uri
var $response_text = null;
// Did the discovery fail miserably?
var $failed = false;
function Auth_Yadis_DiscoveryResult($request_uri)
{
// Initialize the state of the object
// sets all attributes to None except the request_uri
$this->request_uri = $request_uri;
}
function fail()
{
$this->failed = true;
}
function isFailure()
{
return $this->failed;
}
/**
* Returns the list of service objects as described by the XRDS
* document, if this yadis object represents a successful Yadis
* discovery.
*
* @return array $services An array of {@link Auth_Yadis_Service}
* objects
*/
function services()
{
if ($this->xrds) {
return $this->xrds->services();
}
return null;
}
function usedYadisLocation()
{
// Was the Yadis protocol's indirection used?
return ($this->xrds_uri && $this->normalized_uri != $this->xrds_uri);
}
function isXRDS()
{
// Is the response text supposed to be an XRDS document?
return ($this->usedYadisLocation() ||
$this->content_type == Auth_Yadis_CONTENT_TYPE);
}
}
/**
*
* Perform the Yadis protocol on the input URL and return an iterable
* of resulting endpoint objects.
*
* input_url: The URL on which to perform the Yadis protocol
*
* @return: The normalized identity URL and an iterable of endpoint
* objects generated by the filter function.
*
* xrds_parse_func: a callback which will take (uri, xrds_text) and
* return an array of service endpoint objects or null. Usually
* array('Auth_OpenID_ServiceEndpoint', 'fromXRDS').
*
* discover_func: if not null, a callback which should take (uri) and
* return an Auth_Yadis_Yadis object or null.
*/
function Auth_Yadis_getServiceEndpoints($input_url, $xrds_parse_func,
$discover_func=null, $fetcher=null)
{
if ($discover_func === null) {
$discover_function = array('Auth_Yadis_Yadis', 'discover');
}
$yadis_result = call_user_func_array($discover_func,
array($input_url, &$fetcher));
if ($yadis_result === null) {
return array($input_url, array());
}
$endpoints = call_user_func_array($xrds_parse_func,
array($yadis_result->normalized_uri,
$yadis_result->response_text));
if ($endpoints === null) {
$endpoints = array();
}
return array($yadis_result->normalized_uri, $endpoints);
}
/**
* This is the core of the PHP Yadis library. This is the only class
* a user needs to use to perform Yadis discovery. This class
* performs the discovery AND stores the result of the discovery.
*
* First, require this library into your program source:
*
* <pre> require_once "Auth/Yadis/Yadis.php";</pre>
*
* To perform Yadis discovery, first call the "discover" method
* statically with a URI parameter:
*
* <pre> $http_response = array();
* $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
* $yadis_object = Auth_Yadis_Yadis::discover($uri,
* $http_response, $fetcher);</pre>
*
* If the discovery succeeds, $yadis_object will be an instance of
* {@link Auth_Yadis_Yadis}. If not, it will be null. The XRDS
* document found during discovery should have service descriptions,
* which can be accessed by calling
*
* <pre> $service_list = $yadis_object->services();</pre>
*
* which returns an array of objects which describe each service.
* These objects are instances of Auth_Yadis_Service. Each object
* describes exactly one whole Service element, complete with all of
* its Types and URIs (no expansion is performed). The common use
* case for using the service objects returned by services() is to
* write one or more filter functions and pass those to services():
*
* <pre> $service_list = $yadis_object->services(
* array("filterByURI",
* "filterByExtension"));</pre>
*
* The filter functions (whose names appear in the array passed to
* services()) take the following form:
*
* <pre> function myFilter($service) {
* // Query $service object here. Return true if the service
* // matches your query; false if not.
* }</pre>
*
* This is an example of a filter which uses a regular expression to
* match the content of URI tags (note that the Auth_Yadis_Service
* class provides a getURIs() method which you should use instead of
* this contrived example):
*
* <pre>
* function URIMatcher($service) {
* foreach ($service->getElements('xrd:URI') as $uri) {
* if (preg_match("/some_pattern/",
* $service->parser->content($uri))) {
* return true;
* }
* }
* return false;
* }</pre>
*
* The filter functions you pass will be called for each service
* object to determine which ones match the criteria your filters
* specify. The default behavior is that if a given service object
* matches ANY of the filters specified in the services() call, it
* will be returned. You can specify that a given service object will
* be returned ONLY if it matches ALL specified filters by changing
* the match mode of services():
*
* <pre> $yadis_object->services(array("filter1", "filter2"),
* SERVICES_YADIS_MATCH_ALL);</pre>
*
* See {@link SERVICES_YADIS_MATCH_ALL} and {@link
* SERVICES_YADIS_MATCH_ANY}.
*
* Services described in an XRDS should have a library which you'll
* probably be using. Those libraries are responsible for defining
* filters that can be used with the "services()" call. If you need
* to write your own filter, see the documentation for {@link
* Auth_Yadis_Service}.
*
* @package OpenID
*/
class Auth_Yadis_Yadis {
/**
* Returns an HTTP fetcher object. If the CURL extension is
* present, an instance of {@link Auth_Yadis_ParanoidHTTPFetcher}
* is returned. If not, an instance of
* {@link Auth_Yadis_PlainHTTPFetcher} is returned.
*
* If Auth_Yadis_CURL_OVERRIDE is defined, this method will always
* return a {@link Auth_Yadis_PlainHTTPFetcher}.
*/
static function getHTTPFetcher($timeout = 20)
{
if (Auth_Yadis_Yadis::curlPresent() &&
(!defined('Auth_Yadis_CURL_OVERRIDE'))) {
$fetcher = new Auth_Yadis_ParanoidHTTPFetcher($timeout);
} else {
$fetcher = new Auth_Yadis_PlainHTTPFetcher($timeout);
}
return $fetcher;
}
static function curlPresent()
{
return function_exists('curl_init');
}
/**
* @access private
*/
static function _getHeader($header_list, $names)
{
foreach ($header_list as $name => $value) {
foreach ($names as $n) {
if (strtolower($name) == strtolower($n)) {
return $value;
}
}
}
return null;
}
/**
* @access private
*/
static function _getContentType($content_type_header)
{
if ($content_type_header) {
$parts = explode(";", $content_type_header);
return strtolower($parts[0]);
}
}
/**
* This should be called statically and will build a Yadis
* instance if the discovery process succeeds. This implements
* Yadis discovery as specified in the Yadis specification.
*
* @param string $uri The URI on which to perform Yadis discovery.
*
* @param array $http_response An array reference where the HTTP
* response object will be stored (see {@link
* Auth_Yadis_HTTPResponse}.
*
* @param Auth_Yadis_HTTPFetcher $fetcher An instance of a
* Auth_Yadis_HTTPFetcher subclass.
*
* @param array $extra_ns_map An array which maps namespace names
* to namespace URIs to be used when parsing the Yadis XRDS
* document.
*
* @param integer $timeout An optional fetcher timeout, in seconds.
*
* @return mixed $obj Either null or an instance of
* Auth_Yadis_Yadis, depending on whether the discovery
* succeeded.
*/
static function discover($uri, $fetcher,
$extra_ns_map = null, $timeout = 20)
{
$result = new Auth_Yadis_DiscoveryResult($uri);
$request_uri = $uri;
$headers = array("Accept: " . Auth_Yadis_CONTENT_TYPE .
', text/html; q=0.3, application/xhtml+xml; q=0.5');
if ($fetcher === null) {
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher($timeout);
}
$response = $fetcher->get($uri, $headers);
if (!$response || ($response->status != 200 and
$response->status != 206)) {
$result->fail();
return $result;
}
$result->normalized_uri = $response->final_url;
$result->content_type = Auth_Yadis_Yadis::_getHeader(
$response->headers,
array('content-type'));
if ($result->content_type &&
(Auth_Yadis_Yadis::_getContentType($result->content_type) ==
Auth_Yadis_CONTENT_TYPE)) {
$result->xrds_uri = $result->normalized_uri;
} else {
$yadis_location = Auth_Yadis_Yadis::_getHeader(
$response->headers,
array(Auth_Yadis_HEADER_NAME));
if (!$yadis_location) {
$parser = new Auth_Yadis_ParseHTML();
$yadis_location = $parser->getHTTPEquiv($response->body);
}
if ($yadis_location) {
$result->xrds_uri = $yadis_location;
$response = $fetcher->get($yadis_location);
if ((!$response) || ($response->status != 200 and
$response->status != 206)) {
$result->fail();
return $result;
}
$result->content_type = Auth_Yadis_Yadis::_getHeader(
$response->headers,
array('content-type'));
}
}
$result->response_text = $response->body;
return $result;
}
}

View file

@ -1,122 +0,0 @@
Fri Mar 23 11:03:53 PDT 2007 cygnus@janrain.com
* Fix global variables for release
Fri Mar 23 10:37:48 PDT 2007 cygnus@janrain.com
* Update OpenID.pkg
Thu Feb 1 11:36:42 PST 2007 cygnus@janrain.com
* Make tests pass when MySQL connection cannot be established
Mon Jan 15 13:27:20 PST 2007 cygnus@janrain.com
* null-return fix for php4.4.1
Mon Jan 15 13:16:18 PST 2007 cygnus@janrain.com
* Set Auth_OpenID_NO_MATH_SUPPORT in OpenID.php
Thu Jan 11 10:38:16 PST 2007 cygnus@janrain.com
* Add bigmath test values
Wed Jan 3 16:19:47 PST 2007 cygnus@janrain.com
* FIX #1150: Move DH defaults into getters
Wed Jan 3 16:09:39 PST 2007 cygnus@janrain.com
* FIX #1150: Move OpenID namespaces into Auth_OpenID class
Wed Jan 3 16:07:42 PST 2007 cygnus@janrain.com
* Max priority docstring
Wed Jan 3 16:07:00 PST 2007 cygnus@janrain.com
* FIX #1150: Move ns map data into getter
Wed Jan 3 16:04:43 PST 2007 cygnus@janrain.com
* FIX #1150: Move supported XML extension data into getter
Wed Jan 3 16:02:56 PST 2007 cygnus@janrain.com
* FIX #1150: Fix _startswith and _pct_escape_unicode
Wed Jan 3 15:57:51 PST 2007 cygnus@janrain.com
* FIX #1150: Move globals into getter functions in Auth/OpenID/URINorm.php
Wed Jan 3 15:52:16 PST 2007 cygnus@janrain.com
* FIX #1150: Move globals into getter functions in Services/Yadis/Misc.php
Wed Jan 3 15:48:07 PST 2007 cygnus@janrain.com
* FIX #1150: Move globals into getter functions in XRI.php
Wed Mar 21 16:58:47 PDT 2007 cygnus@janrain.com
* Apply Simon Willison's fix to the plain fetcher POST handling
Wed Mar 14 16:26:23 PDT 2007 cygnus@janrain.com
* Fix include_path in example server server.php
Wed Mar 14 16:19:05 PDT 2007 cygnus@janrain.com
* Fix include_path in example server setup.php
Mon Mar 12 11:30:39 PDT 2007 cygnus@janrain.com
* Unify php.net links
Mon Mar 12 11:27:26 PDT 2007 cygnus@janrain.com
* Remove redundant detect.php text because it needs to be first
Mon Mar 12 11:23:11 PDT 2007 cygnus@janrain.com
* Update PHP versions in README
Mon Mar 12 11:23:04 PDT 2007 cygnus@janrain.com
* Add note to README for using detect script
Mon Mar 12 11:33:10 PDT 2007 cygnus@cprogrammer.org
* Make it very clear what the result is
Mon Mar 12 11:31:06 PDT 2007 cygnus@cprogrammer.org
* Whitespace
Mon Mar 12 11:30:56 PDT 2007 cygnus@cprogrammer.org
* Say CURL isn't required
Mon Mar 12 11:30:42 PDT 2007 cygnus@cprogrammer.org
* Remove comment
Mon Mar 12 11:30:34 PDT 2007 cygnus@cprogrammer.org
* Include yadis code and complain if it isn't found
Mon Mar 12 11:30:05 PDT 2007 cygnus@cprogrammer.org
* Mention absence of open_basedir
Mon Mar 12 11:29:54 PDT 2007 cygnus@cprogrammer.org
* Boldify web_user
Mon Mar 12 11:29:46 PDT 2007 cygnus@cprogrammer.org
* Mention file store
Mon Mar 12 11:29:35 PDT 2007 cygnus@cprogrammer.org
* Boldify text
Mon Mar 12 11:29:26 PDT 2007 cygnus@cprogrammer.org
* Typo fix
Mon Mar 12 10:31:01 PDT 2007 cygnus@janrain.com
* README tweak
Mon Mar 12 10:30:44 PDT 2007 cygnus@janrain.com
* Update README with CONTRIBUTING section
Mon Mar 12 10:14:11 PDT 2007 cygnus@janrain.com
* Only try to include one file in inclusion test
Mon Mar 12 10:07:52 PDT 2007 cygnus@janrain.com
* Remove HTML tags from format-agnostic output
Fri Feb 16 02:18:49 PST 2007 atrus@atrus.org
* Improve HTML parser effeciency and tolerence
Wed Feb 7 16:12:19 PST 2007 cygnus@janrain.com
* Add note about open_basedir
Tue Jan 16 09:45:46 PST 2007 cygnus@janrain.com
* Fix example consumer on Windows
Mon Jan 15 13:13:59 PST 2007 cygnus@janrain.com
* BigMath no longer triggers error on no math support
Fri Mar 23 10:28:00 PDT 2007 cygnus@janrain.com
tagged 1.2.1

View file

@ -0,0 +1,51 @@
* API Changes
* AX::FetchResponse::fromSuccessResponse - return null when AX
response arguments are absent
* Alter AX fromOpenIDRequest() to take Auth_OpenID_AuthRequest
object instead of Auth_OpenID_Message object so that it matches
its counterpart methods in SREG and PAPE extensions.
* PAPE (Provider Authentication Policy Extension) module
* Updated extension for specification draft 2
* Auth_OpenID_PAPE_Request::fromSuccessResponse returns None if
PAPE response arguments were not signed
* Added functions to generate request/response HTML forms with
auto-submission javascript
* Consumer (relying party) API:
Auth_OpenID_AuthRequest::htmlMarkup
* Server API: Auth_OpenID_OpenIDResponse::toHTML
* New Features
* Added examples/discover.php, an OpenID service discovery tool
* Add optional form_tag_attrs argument to
Auth_OpenID_ServerResponse::toFormMarkup for setting arbitrary
FORM element attributes
* Fetchers now only read/request first megabyte of response
* Bug Fixes
* NOT NULL constraints were added to SQLStore tables where
appropriate
* Yadis discovery now properly falls back to HTML-based discovery if
it fails to get an XRDS document
* Auth_OpenID_Decoder now behaves correctly when given a protocol
message with an invalid OpenID namespace or a missing OpenID mode
* Auth_OpenID_OpenIDResponse::toFormMarkup: Use return_to from the
request, not the response fields (Not all responses (i.e. cancel,
setup_needed) include a return_to field.)
* normalize return_to URL before performing return_to verification
* Auth_OpenID_Consumer::_verifyDiscoveryResults: fall back to OpenID
1.0 type if 1.1 endpoint cannot be found
* Auth_Yadis_ParanoidHTTPFetcher now works correctly with both array
and non-array CURL versions
* Clarified licensing language in all source files
* OpenID 1 association requests no longer explicitly set
no-encryption session type
* Auth_OpenID_ServiceEndpoint::getDisplayIdentifier no longer
includes a fragment, if present, in display identifiers
* check_authentication requests: copy entire response, not just
signed fields. Fixes missing namespace in check_authentication
requests
* Yadis discovery now includes application/xhtml+xml and qualities
in the Accept header
* Normalize URLs correctly with URINorm.php
* Auth_OpenID_MySQLStore: Use ENGINE instead of TYPE when creating
tables

View file

@ -1,24 +1,202 @@
PHP OpenID - OpenID consumer and server library
Copyright (C) 2005 Janrain, Inc.
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
1. Definitions.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
More info about PHP OpenID:
eng+openid@janrain.com
http://www.openidenabled.com/openid/libraries/php/
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
More info about OpenID:
http://www.openid.net
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -0,0 +1,85 @@
What's New in PHP OpenID 2.0
============================
This library implements both the OpenID 1 and OpenID 2 protocols. The
API changes in this version of the library are minimal and mostly
backwards-compatible with current RP and Server implementations. If
you're already using this library for OpenID 1, only a few small
changes (see Summary below) will be needed once you upgrade.
The big news here is compatibility with version 2.0 of the OpenID
protocol. Highlights include:
* Simple Registration support in a new module Auth/OpenID/SReg.php.
(Those previously using
Auth_OpenID_SuccessResponse::extensionResponse() are advised to
look here and at the example consumer and server for detailed usage
information.)
* OpenID provider-driven identifier selection.
* "Negotiators" allow you to define which association types to use.
* Improved examples/detect.php script (bugs fixed)
* Improved layout of example consumer (see examples/consumer)
* An improved HTML parser implementation
* Library is now immune to being included inside functions and
methods
* Fixes to avoid multibyte overloading problems
If you've written your own custom store or code that interacts
directly with it, you'll need to review the change notes for
Auth_OpenID_Interface in Auth/OpenID/Interface.php.
Upgrading from earlier versions of this library
-----------------------------------------------
One of the additions to the OpenID protocol was a specified nonce
format for one-way nonces. As a result, the nonce table in the
SQL-driven stores has changed. You'll need to run the Python script
contrib/upgrade-store-1.1-to-2.0 to upgrade your store, or you'll
encounter errors about the wrong number of columns in the oid_nonces
table. To run the script, you'll need a python module supporting your
database type: pysqlite2, psycopg, or MySQLdb.
If you cannot run the Python script, you can re-create your store by
dropping the tables in the store and calling createTables() on the
store object.
Consumers should now pass the consumer return_to URL to
Auth_OpenID_Consumer::complete() to defend against return_to URL
tampering. This has REPLACED the old parameter, $query. $query is
now a second optional parameter. It is STRONGLY RECOMMENDED that you
never override $query, since the OpenID library uses its own logic to
sidestep PHP's broken request-processing code.
Summary of API Changes
----------------------
- Auth_OpenID::fixArgs is now no longer necessary, and
Auth_OpenID_Consumer::complete and Auth_OpenID_Server::decodeRequest
no longer take query argument arrays. *You should no longer pass any
parameters to these methods.*
- Auth_OpenID_SuccessResponse::extensionResponse() is no longer the
preferred way to extract extension response parameters from the OpenID
response. Instead, see the Auth/OpenID/SReg.php module and the
example consumer and server for detailed usage information on
constructing Simple Registration requests and inspecting responses.
extensionResponse() is still valid, but now takes a second parameter
(bool) indicating whether extension args should be signed.
- The Auth_OpenID_Server's response answer() method now takes
additional parameters to support provider-driven identifier selection.
See the example server and the documentation for
Auth_OpenID_CheckIDRequest::answer.
- Auth_OpenID_Consumer::complete() now takes two args:
- $return_to, a required string that is the return URL passed to
Auth_OpenID_AuthRequest::redirectURL()
- $query, an optional array (or null if absent) denoting the query
parameters of the OpenID response. If null, the response data
will be extracted from the PHP request environment. Library
users SHOULD NOT ever pass anything for $query unless they're
testing the library.

View file

@ -46,17 +46,7 @@ Follow these steps:
- Install either the DOM or domxml PHP XML processing extension,
but not both (they are incompatible).
2. Use the PEAR installer to install the package. To use the PEAR
installer, run this command from the package directory:
# pear install package.xml
If your PEAR version is sufficiently new, you can run this instead:
# pear install package2.xml
If the PEAR installer isn't available, you can copy the Auth/ and
Services/ directories into your PHP include path.
2. Copy the Auth/ directory into your PHP include path.
TESTING YOUR SETUP
==================
@ -75,28 +65,48 @@ examples/README file for more details.
TROUBLESHOOTING
===============
On some systems, PHP basedir restrictions prevent web servers from
* If you're unable to use an OpenID URL with the library, you may want
to try using the discover tool (examples/discover.php). This tool
will perform OpenID discovery on the identifier and give a list of
discovered OpenID services and their types.
* On some systems, PHP basedir restrictions prevent web servers from
opening a source of randomness, such as /dev/urandom. If your PHP
OpenID library has trouble getting a satisfactory source of
randomness, check your Apache and PHP configurations to be sure that
the randomness source is in the list of allowed paths for the
"open_basedir" option.
* In some cases, bugs in the GMP math library will result in signature
validation errors when using this library. Since GMP is preferred
over bcmath (for performance), you will have to define
Auth_OpenID_BUGGY_GMP in your application *before* importing any of
the library code:
define('Auth_OpenID_BUGGY_GMP', true);
* Not all PHP installations support SSL. You can find out if yours
supports SSL by reading the "HTTP Fetching" section of the output of
"examples/detect.php." If your installation does not support SSL,
then https:// identity URLs and server URLs will not be supported by
the library. An attempt to use such an identity URL will be
equivalent to using an invalid OpenID. To enable SSL support,
recompile PHP with OpenSSL support or install the appropriate OpenSSL
module for your platform. If you are using CURL, CURL will need to be
built with OpenSSL support.
GETTING HELP
============
If you have any questions, recommendations, or patches, please tell
us! Subscribe to our OpenID development discussion list at
http://lists.openidenabled.com/mailman/listinfo/dev
http://openid.net/developers/dev-mailing-lists/
DOCUMENTATION
=============
You can view the HTML library documentation in the doc/ directory.
You can also find it on the web at
http://www.openidenabled.com/resources/docs/openid/php/
This package's documentation is in PhpDoc format. To generate the
documentation, install phpdoc and run the admin/makedoc.sh script.
@ -110,7 +120,11 @@ CONTRIBUTING
If you have a bugfix or feature you'd like to contribute, don't
hesitate to send it to us. Post your patch to the development list at
http://lists.openidenabled.com/mailman/listinfo/dev
http://openid.net/developers/dev-mailing-lists/
For more detailed information on how to contribute, see
http://openidenabled.com/contribute/
To run the test suite included with this package, install PHPUnit 1.x
and run
@ -119,4 +133,4 @@ and run
PHPUnit 1.x can be found at
http://pear.phpunit.de/get/
http://pear.phpunit.de/get/

View file

@ -7,4 +7,9 @@ files may be replaced by those found at the location specified in the
COPYING file.
Rob Richards
rrichards@ctindustries.net
rrichards@ctindustries.net
22.03.2012 Grischa Brockhaus
* Replaced with newest version of library
* Added this pull request changes https://github.com/arnaud-lb/php-openid/commit/bb6da3326a330b2d4dd235a23316d23bc5569016
(Fixing some big problems with new ERROR HANDLING in S9Y 1.7)

View file

@ -0,0 +1,7 @@
GitHub is the new home for php-openid library development.
This library was originally written by JanRain and managed using the darcs RCS. This file is home to notes regarding the migration from darcs to git, and the move from openidenabled.com to github.com.
Contact:
Brian Ellin
brian@janrain.com

View file

@ -1,92 +0,0 @@
<?php
/**
* This module contains the HTTP fetcher interface
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package Yadis
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
class Services_Yadis_HTTPResponse {
function Services_Yadis_HTTPResponse($final_url = null, $status = null,
$headers = null, $body = null)
{
$this->final_url = $final_url;
$this->status = $status;
$this->headers = $headers;
$this->body = $body;
}
}
/**
* This class is the interface for HTTP fetchers the Yadis library
* uses. This interface is only important if you need to write a new
* fetcher for some reason.
*
* @access private
* @package Yadis
*/
class Services_Yadis_HTTPFetcher {
var $timeout = 20; // timeout in seconds.
/**
* Return whether a URL should be allowed. Override this method to
* conform to your local policy.
*
* By default, will attempt to fetch any http or https URL.
*/
function allowedURL($url)
{
return $this->URLHasAllowedScheme($url);
}
/**
* Is this an http or https URL?
*
* @access private
*/
function URLHasAllowedScheme($url)
{
return (bool)preg_match('/^https?:\/\//i', $url);
}
/**
* @access private
*/
function _findRedirect($headers)
{
foreach ($headers as $line) {
if (strpos($line, "Location: ") === 0) {
$parts = explode(" ", $line, 2);
return $parts[1];
}
}
return null;
}
/**
* Fetches the specified URL using optional extra headers and
* returns the server's response.
*
* @param string $url The URL to be fetched.
* @param array $extra_headers An array of header strings
* (e.g. "Accept: text/html").
* @return mixed $result An array of ($code, $url, $headers,
* $body) if the URL could be fetched; null if the URL does not
* pass the URLHasAllowedScheme check or if the server's response
* is malformed.
*/
function get($url, $headers)
{
trigger_error("not implemented", E_USER_ERROR);
}
}
?>

View file

@ -1,177 +0,0 @@
<?php
/**
* This module contains the CURL-based HTTP fetcher implementation.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package Yadis
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
/**
* Interface import
*/
require_once "Services/Yadis/HTTPFetcher.php";
/**
* A paranoid {@link Services_Yadis_HTTPFetcher} class which uses CURL
* for fetching.
*
* @package Yadis
*/
class Services_Yadis_ParanoidHTTPFetcher extends Services_Yadis_HTTPFetcher {
function Services_Yadis_ParanoidHTTPFetcher()
{
$this->reset();
}
function reset()
{
$this->headers = array();
$this->data = "";
}
/**
* @access private
*/
function _writeHeader($ch, $header)
{
array_push($this->headers, rtrim($header));
return strlen($header);
}
/**
* @access private
*/
function _writeData($ch, $data)
{
$this->data .= $data;
return strlen($data);
}
function get($url, $extra_headers = null)
{
$stop = time() + $this->timeout;
$off = $this->timeout;
$redir = true;
while ($redir && ($off > 0)) {
$this->reset();
$c = curl_init();
if (defined('CURLOPT_NOSIGNAL')) {
curl_setopt($c, CURLOPT_NOSIGNAL, true);
}
if (!$this->allowedURL($url)) {
trigger_error(sprintf("Fetching URL not allowed: %s", $url),
E_USER_WARNING);
return null;
}
curl_setopt($c, CURLOPT_WRITEFUNCTION,
array(&$this, "_writeData"));
curl_setopt($c, CURLOPT_HEADERFUNCTION,
array(&$this, "_writeHeader"));
if ($extra_headers) {
curl_setopt($c, CURLOPT_HTTPHEADER, $extra_headers);
}
curl_setopt($c, CURLOPT_TIMEOUT, $off);
curl_setopt($c, CURLOPT_URL, $url);
curl_exec($c);
$code = curl_getinfo($c, CURLINFO_HTTP_CODE);
$body = $this->data;
$headers = $this->headers;
if (!$code) {
return null;
}
if (in_array($code, array(301, 302, 303, 307))) {
$url = $this->_findRedirect($headers);
$redir = true;
} else {
$redir = false;
curl_close($c);
$new_headers = array();
foreach ($headers as $header) {
if (preg_match("/:/", $header)) {
list($name, $value) = explode(": ", $header, 2);
$new_headers[$name] = $value;
}
}
return new Services_Yadis_HTTPResponse($url, $code,
$new_headers, $body);
}
$off = $stop - time();
}
trigger_error(sprintf("Timed out fetching: %s", $url),
E_USER_WARNING);
return null;
}
function post($url, $body)
{
$this->reset();
if (!$this->allowedURL($url)) {
trigger_error(sprintf("Fetching URL not allowed: %s", $url),
E_USER_WARNING);
return null;
}
$c = curl_init();
curl_setopt($c, CURLOPT_NOSIGNAL, true);
curl_setopt($c, CURLOPT_POST, true);
curl_setopt($c, CURLOPT_POSTFIELDS, $body);
curl_setopt($c, CURLOPT_TIMEOUT, $this->timeout);
curl_setopt($c, CURLOPT_URL, $url);
curl_setopt($c, CURLOPT_WRITEFUNCTION,
array(&$this, "_writeData"));
curl_exec($c);
$code = curl_getinfo($c, CURLINFO_HTTP_CODE);
if (!$code) {
trigger_error("No HTTP code returned", E_USER_WARNING);
return null;
}
$body = $this->data;
curl_close($c);
$new_headers = array();
foreach ($this->headers as $header) {
if (preg_match("/:/", $header)) {
list($name, $value) = explode(": ", $header, 2);
$new_headers[$name] = $value;
}
}
return new Services_Yadis_HTTPResponse($url, $code,
$new_headers, $body);
}
}
?>

View file

@ -1,313 +0,0 @@
<?php
/**
* The core PHP Yadis implementation.
*
* PHP versions 4 and 5
*
* LICENSE: See the COPYING file included in this distribution.
*
* @package Yadis
* @author JanRain, Inc. <openid@janrain.com>
* @copyright 2005 Janrain, Inc.
* @license http://www.gnu.org/copyleft/lesser.html LGPL
*/
/**
* Need both fetcher types so we can use the right one based on the
* presence or absence of CURL.
*/
require_once "Services/Yadis/PlainHTTPFetcher.php";
require_once "Services/Yadis/ParanoidHTTPFetcher.php";
/**
* Need this for parsing HTML (looking for META tags).
*/
require_once "Services/Yadis/ParseHTML.php";
/**
* Need this to parse the XRDS document during Yadis discovery.
*/
require_once "Services/Yadis/XRDS.php";
/**
* This is the core of the PHP Yadis library. This is the only class
* a user needs to use to perform Yadis discovery. This class
* performs the discovery AND stores the result of the discovery.
*
* First, require this library into your program source:
*
* <pre> require_once "Services/Yadis/Yadis.php";</pre>
*
* To perform Yadis discovery, first call the "discover" method
* statically with a URI parameter:
*
* <pre> $http_response = array();
* $fetcher = Services_Yadis_Yadis::getHTTPFetcher();
* $yadis_object = Services_Yadis_Yadis::discover($uri,
* $http_response, $fetcher);</pre>
*
* If the discovery succeeds, $yadis_object will be an instance of
* {@link Services_Yadis_Yadis}. If not, it will be null. The XRDS
* document found during discovery should have service descriptions,
* which can be accessed by calling
*
* <pre> $service_list = $yadis_object->services();</pre>
*
* which returns an array of objects which describe each service.
* These objects are instances of Services_Yadis_Service. Each object
* describes exactly one whole Service element, complete with all of
* its Types and URIs (no expansion is performed). The common use
* case for using the service objects returned by services() is to
* write one or more filter functions and pass those to services():
*
* <pre> $service_list = $yadis_object->services(
* array("filterByURI",
* "filterByExtension"));</pre>
*
* The filter functions (whose names appear in the array passed to
* services()) take the following form:
*
* <pre> function myFilter(&$service) {
* // Query $service object here. Return true if the service
* // matches your query; false if not.
* }</pre>
*
* This is an example of a filter which uses a regular expression to
* match the content of URI tags (note that the Services_Yadis_Service
* class provides a getURIs() method which you should use instead of
* this contrived example):
*
* <pre>
* function URIMatcher(&$service) {
* foreach ($service->getElements('xrd:URI') as $uri) {
* if (preg_match("/some_pattern/",
* $service->parser->content($uri))) {
* return true;
* }
* }
* return false;
* }</pre>
*
* The filter functions you pass will be called for each service
* object to determine which ones match the criteria your filters
* specify. The default behavior is that if a given service object
* matches ANY of the filters specified in the services() call, it
* will be returned. You can specify that a given service object will
* be returned ONLY if it matches ALL specified filters by changing
* the match mode of services():
*
* <pre> $yadis_object->services(array("filter1", "filter2"),
* SERVICES_YADIS_MATCH_ALL);</pre>
*
* See {@link SERVICES_YADIS_MATCH_ALL} and {@link
* SERVICES_YADIS_MATCH_ANY}.
*
* Services described in an XRDS should have a library which you'll
* probably be using. Those libraries are responsible for defining
* filters that can be used with the "services()" call. If you need
* to write your own filter, see the documentation for {@link
* Services_Yadis_Service}.
*
* @package Yadis
*/
class Services_Yadis_Yadis {
/**
* Returns an HTTP fetcher object. If the CURL extension is
* present, an instance of {@link Services_Yadis_ParanoidHTTPFetcher}
* is returned. If not, an instance of
* {@link Services_Yadis_PlainHTTPFetcher} is returned.
*/
function getHTTPFetcher($timeout = 20)
{
if (Services_Yadis_Yadis::curlPresent()) {
$fetcher = new Services_Yadis_ParanoidHTTPFetcher($timeout);
} else {
$fetcher = new Services_Yadis_PlainHTTPFetcher($timeout);
}
return $fetcher;
}
function curlPresent()
{
return function_exists('curl_init');
}
/**
* @access private
*/
function _getHeader($header_list, $names)
{
foreach ($header_list as $name => $value) {
foreach ($names as $n) {
if (strtolower($name) == strtolower($n)) {
return $value;
}
}
}
return null;
}
/**
* @access private
*/
function _getContentType($content_type_header)
{
if ($content_type_header) {
$parts = explode(";", $content_type_header);
return strtolower($parts[0]);
}
}
/**
* This should be called statically and will build a Yadis
* instance if the discovery process succeeds. This implements
* Yadis discovery as specified in the Yadis specification.
*
* @param string $uri The URI on which to perform Yadis discovery.
*
* @param array $http_response An array reference where the HTTP
* response object will be stored (see {@link
* Services_Yadis_HTTPResponse}.
*
* @param Services_Yadis_HTTPFetcher $fetcher An instance of a
* Services_Yadis_HTTPFetcher subclass.
*
* @param array $extra_ns_map An array which maps namespace names
* to namespace URIs to be used when parsing the Yadis XRDS
* document.
*
* @param integer $timeout An optional fetcher timeout, in seconds.
*
* @return mixed $obj Either null or an instance of
* Services_Yadis_Yadis, depending on whether the discovery
* succeeded.
*/
function discover($uri, &$http_response, &$fetcher,
$extra_ns_map = null, $timeout = 20)
{
if (!$uri) {
return null;
}
$request_uri = $uri;
$headers = array("Accept: application/xrds+xml");
if (!$fetcher) {
$fetcher = Services_Yadis_Yadis::getHTTPFetcher($timeout);
}
$response = $fetcher->get($uri, $headers);
$http_response = $response;
if (!$response) {
return null;
}
if ($response->status != 200) {
return null;
}
$xrds_uri = $response->final_url;
$uri = $response->final_url;
$body = $response->body;
$xrds_header_uri = Services_Yadis_Yadis::_getHeader(
$response->headers,
array('x-xrds-location',
'x-yadis-location'));
$content_type = Services_Yadis_Yadis::_getHeader($response->headers,
array('content-type'));
if ($xrds_header_uri) {
$xrds_uri = $xrds_header_uri;
$response = $fetcher->get($xrds_uri);
$http_response = $response;
if (!$response) {
return null;
} else {
$body = $response->body;
$headers = $response->headers;
$content_type = Services_Yadis_Yadis::_getHeader($headers,
array('content-type'));
}
}
if (Services_Yadis_Yadis::_getContentType($content_type) !=
'application/xrds+xml') {
// Treat the body as HTML and look for a META tag.
$parser = new Services_Yadis_ParseHTML();
$new_uri = $parser->getHTTPEquiv($body);
$xrds_uri = null;
if ($new_uri) {
$response = $fetcher->get($new_uri);
if ($response->status != 200) {
return null;
}
$http_response = $response;
$body = $response->body;
$xrds_uri = $new_uri;
$content_type = Services_Yadis_Yadis::_getHeader(
$response->headers,
array('content-type'));
}
}
$xrds = Services_Yadis_XRDS::parseXRDS($body, $extra_ns_map);
if ($xrds !== null) {
$y = new Services_Yadis_Yadis();
$y->request_uri = $request_uri;
$y->xrds = $xrds;
$y->uri = $uri;
$y->xrds_uri = $xrds_uri;
$y->body = $body;
$y->content_type = $content_type;
return $y;
} else {
return null;
}
}
/**
* Instantiates an empty Services_Yadis_Yadis object. This
* constructor should not be used by any user of the library.
* This constructor results in a completely useless object which
* must be populated with valid discovery information. Instead of
* using this constructor, call
* Services_Yadis_Yadis::discover($uri).
*/
function Services_Yadis_Yadis()
{
$this->request_uri = null;
$this->uri = null;
$this->xrds = null;
$this->xrds_uri = null;
$this->body = null;
$this->content_type = null;
}
/**
* Returns the list of service objects as described by the XRDS
* document, if this yadis object represents a successful Yadis
* discovery.
*
* @return array $services An array of {@link Services_Yadis_Service}
* objects
*/
function services()
{
if ($this->xrds) {
return $this->xrds->services();
}
return null;
}
}
?>

View file

@ -1,187 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<package version="1.0">
<name>Auth_OpenID</name>
<summary>PHP OpenID</summary>
<description>
An implementation of the OpenID single sign-on authentication protocol.
</description>
<license>LGPL</license>
<maintainers>
<maintainer>
<user>cygnus</user>
<name>Jonathan Daugherty</name>
<email>cygnus@janrain.com</email>
<role>lead</role>
</maintainer>
<maintainer>
<user>jhoyt</user>
<name>Josh Hoyt</name>
<email>josh@janrain.com</email>
<role>lead</role>
</maintainer>
</maintainers>
<release>
<version>1.2.2</version>
<date>2007-03-23</date>
<state>stable</state>
<notes>
<![CDATA[
Wed Mar 21 16:58:47 PDT 2007 cygnus@janrain.com
* Apply Simon Willison's fix to the plain fetcher POST handling
Wed Mar 14 16:26:23 PDT 2007 cygnus@janrain.com
* Fix include_path in example server server.php
Wed Mar 14 16:19:05 PDT 2007 cygnus@janrain.com
* Fix include_path in example server setup.php
Mon Mar 12 11:30:39 PDT 2007 cygnus@janrain.com
* Unify php.net links
Mon Mar 12 11:27:26 PDT 2007 cygnus@janrain.com
* Remove redundant detect.php text because it needs to be first
Mon Mar 12 11:23:11 PDT 2007 cygnus@janrain.com
* Update PHP versions in README
Mon Mar 12 11:23:04 PDT 2007 cygnus@janrain.com
* Add note to README for using detect script
Mon Mar 12 11:33:10 PDT 2007 cygnus@cprogrammer.org
* Make it very clear what the result is
Mon Mar 12 11:31:06 PDT 2007 cygnus@cprogrammer.org
* Whitespace
Mon Mar 12 11:30:56 PDT 2007 cygnus@cprogrammer.org
* Say CURL isn't required
Mon Mar 12 11:30:42 PDT 2007 cygnus@cprogrammer.org
* Remove comment
Mon Mar 12 11:30:34 PDT 2007 cygnus@cprogrammer.org
* Include yadis code and complain if it isn't found
Mon Mar 12 11:30:05 PDT 2007 cygnus@cprogrammer.org
* Mention absence of open_basedir
Mon Mar 12 11:29:54 PDT 2007 cygnus@cprogrammer.org
* Boldify web_user
Mon Mar 12 11:29:46 PDT 2007 cygnus@cprogrammer.org
* Mention file store
Mon Mar 12 11:29:35 PDT 2007 cygnus@cprogrammer.org
* Boldify text
Mon Mar 12 11:29:26 PDT 2007 cygnus@cprogrammer.org
* Typo fix
Mon Mar 12 10:31:01 PDT 2007 cygnus@janrain.com
* README tweak
Mon Mar 12 10:30:44 PDT 2007 cygnus@janrain.com
* Update README with CONTRIBUTING section
Mon Mar 12 10:14:11 PDT 2007 cygnus@janrain.com
* Only try to include one file in inclusion test
Mon Mar 12 10:07:52 PDT 2007 cygnus@janrain.com
* Remove HTML tags from format-agnostic output
Fri Feb 16 02:18:49 PST 2007 atrus@atrus.org
* Improve HTML parser effeciency and tolerence
Wed Feb 7 16:12:19 PST 2007 cygnus@janrain.com
* Add note about open_basedir
Tue Jan 16 09:45:46 PST 2007 cygnus@janrain.com
* Fix example consumer on Windows
Mon Jan 15 13:13:59 PST 2007 cygnus@janrain.com
* BigMath no longer triggers error on no math support
Fri Mar 23 10:28:00 PDT 2007 cygnus@janrain.com
tagged 1.2.1
]]>
</notes>
<filelist><dir name="/" baseinstalldir="Auth">
<dir name="Auth">
<dir name="OpenID">
<file name="DatabaseConnection.php" role="php" />
<file name="Association.php" role="php" />
<file name="BigMath.php" role="php" />
<file name="Consumer.php" role="php" />
<file name="CryptUtil.php" role="php" />
<file name="DiffieHellman.php" role="php" />
<file name="Discover.php" role="php" />
<file name="DumbStore.php" role="php" />
<file name="FileStore.php" role="php" />
<file name="HMACSHA1.php" role="php" />
<file name="Interface.php" role="php" />
<file name="KVForm.php" role="php" />
<file name="MySQLStore.php" role="php" />
<file name="Parse.php" role="php" />
<file name="PostgreSQLStore.php" role="php" />
<file name="SQLStore.php" role="php" />
<file name="SQLiteStore.php" role="php" />
<file name="Server.php" role="php" />
<file name="ServerRequest.php" role="php" />
<file name="TrustRoot.php" role="php" />
<file name="URINorm.php" role="php" />
</dir>
<file name="OpenID.php" role="php" />
</dir>
<dir name="Services">
<dir name="Yadis">
<file name="Misc.php" role="php" />
<file name="ParanoidHTTPFetcher.php" role="php" />
<file name="HTTPFetcher.php" role="php" />
<file name="Manager.php" role="php" />
<file name="XRIRes.php" role="php" />
<file name="PlainHTTPFetcher.php" role="php" />
<file name="ParseHTML.php" role="php" />
<file name="XML.php" role="php" />
<file name="XRDS.php" role="php" />
<file name="XRI.php" role="php" />
<file name="Yadis.php" role="php" />
</dir>
</dir>
<dir name="doc" role="doc">
</dir>
<dir name="examples" role="doc">
<dir name="consumer" role="doc">
<file name="finish_auth.php" role="doc" />
<file name="common.php" role="doc" />
<file name="index.php" role="doc" />
<file name="try_auth.php" role="doc" />
</dir>
<dir name="server" role="doc">
<dir name="lib" role="doc">
<dir name="render" role="doc">
<file name="about.php" role="doc" />
<file name="login.php" role="doc" />
<file name="sites.php" role="doc" />
<file name="trust.php" role="doc" />
</dir>
<file name="actions.php" role="doc" />
<file name="common.php" role="doc" />
<file name="render.php" role="doc" />
<file name="session.php" role="doc" />
</dir>
<file name="openid-server.css" role="doc" />
<file name="index.php" role="doc" />
<file name="server.php" role="doc" />
<file name="setup.php" role="doc" />
</dir>
<file name="detect.php" role="doc" />
<file name="README" role="doc" />
</dir>
</dir></filelist>
</release>
<deps>
<dep type="php" rel="ge" version="4.3.0" />
</deps>
</package>

View file

@ -1,237 +0,0 @@
<?xml version="1.0"?>
<package 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>Auth_OpenID</name>
<uri>http://www.openidenabled.com/resources/downloads/php-openid/pear/Auth_OpenID-1.2.2.tgz</uri>
<summary>PHP OpenID</summary>
<description>
An implementation of the OpenID single sign-on authentication protocol.
</description>
<lead>
<name>Jonathan Daugherty</name>
<user>cygnus</user>
<email>cygnus@janrain.com</email>
<active>yes</active>
</lead>
<lead>
<name>Josh Hoyt</name>
<user>jhoyt</user>
<email>josh@janrain.com</email>
<active>yes</active>
</lead>
<date>2007-03-23</date>
<version>
<release>1.2.2</release>
<api>1.2.2</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://www.gnu.org/copyleft/lesser.txt">LGPL</license>
<notes>
<![CDATA[
Wed Mar 21 16:58:47 PDT 2007 cygnus@janrain.com
* Apply Simon Willison's fix to the plain fetcher POST handling
Wed Mar 14 16:26:23 PDT 2007 cygnus@janrain.com
* Fix include_path in example server server.php
Wed Mar 14 16:19:05 PDT 2007 cygnus@janrain.com
* Fix include_path in example server setup.php
Mon Mar 12 11:30:39 PDT 2007 cygnus@janrain.com
* Unify php.net links
Mon Mar 12 11:27:26 PDT 2007 cygnus@janrain.com
* Remove redundant detect.php text because it needs to be first
Mon Mar 12 11:23:11 PDT 2007 cygnus@janrain.com
* Update PHP versions in README
Mon Mar 12 11:23:04 PDT 2007 cygnus@janrain.com
* Add note to README for using detect script
Mon Mar 12 11:33:10 PDT 2007 cygnus@cprogrammer.org
* Make it very clear what the result is
Mon Mar 12 11:31:06 PDT 2007 cygnus@cprogrammer.org
* Whitespace
Mon Mar 12 11:30:56 PDT 2007 cygnus@cprogrammer.org
* Say CURL isn't required
Mon Mar 12 11:30:42 PDT 2007 cygnus@cprogrammer.org
* Remove comment
Mon Mar 12 11:30:34 PDT 2007 cygnus@cprogrammer.org
* Include yadis code and complain if it isn't found
Mon Mar 12 11:30:05 PDT 2007 cygnus@cprogrammer.org
* Mention absence of open_basedir
Mon Mar 12 11:29:54 PDT 2007 cygnus@cprogrammer.org
* Boldify web_user
Mon Mar 12 11:29:46 PDT 2007 cygnus@cprogrammer.org
* Mention file store
Mon Mar 12 11:29:35 PDT 2007 cygnus@cprogrammer.org
* Boldify text
Mon Mar 12 11:29:26 PDT 2007 cygnus@cprogrammer.org
* Typo fix
Mon Mar 12 10:31:01 PDT 2007 cygnus@janrain.com
* README tweak
Mon Mar 12 10:30:44 PDT 2007 cygnus@janrain.com
* Update README with CONTRIBUTING section
Mon Mar 12 10:14:11 PDT 2007 cygnus@janrain.com
* Only try to include one file in inclusion test
Mon Mar 12 10:07:52 PDT 2007 cygnus@janrain.com
* Remove HTML tags from format-agnostic output
Fri Feb 16 02:18:49 PST 2007 atrus@atrus.org
* Improve HTML parser effeciency and tolerence
Wed Feb 7 16:12:19 PST 2007 cygnus@janrain.com
* Add note about open_basedir
Tue Jan 16 09:45:46 PST 2007 cygnus@janrain.com
* Fix example consumer on Windows
Mon Jan 15 13:13:59 PST 2007 cygnus@janrain.com
* BigMath no longer triggers error on no math support
Fri Mar 23 10:28:00 PDT 2007 cygnus@janrain.com
tagged 1.2.1
]]>
</notes>
<contents>
<dir name="/">
<dir name="Auth">
<dir name="OpenID">
<file name="DatabaseConnection.php" role="php" />
<file name="Association.php" role="php" />
<file name="BigMath.php" role="php" />
<file name="Consumer.php" role="php" />
<file name="CryptUtil.php" role="php" />
<file name="DiffieHellman.php" role="php" />
<file name="Discover.php" role="php" />
<file name="DumbStore.php" role="php" />
<file name="FileStore.php" role="php" />
<file name="HMACSHA1.php" role="php" />
<file name="Interface.php" role="php" />
<file name="KVForm.php" role="php" />
<file name="MySQLStore.php" role="php" />
<file name="Parse.php" role="php" />
<file name="PostgreSQLStore.php" role="php" />
<file name="SQLStore.php" role="php" />
<file name="SQLiteStore.php" role="php" />
<file name="Server.php" role="php" />
<file name="ServerRequest.php" role="php" />
<file name="TrustRoot.php" role="php" />
<file name="URINorm.php" role="php" />
</dir>
<file name="OpenID.php" role="php" />
</dir>
<dir name="Services">
<dir name="Yadis">
<file name="Misc.php" role="php" />
<file name="ParanoidHTTPFetcher.php" role="php" />
<file name="HTTPFetcher.php" role="php" />
<file name="Manager.php" role="php" />
<file name="XRIRes.php" role="php" />
<file name="PlainHTTPFetcher.php" role="php" />
<file name="ParseHTML.php" role="php" />
<file name="XML.php" role="php" />
<file name="XRDS.php" role="php" />
<file name="XRI.php" role="php" />
<file name="Yadis.php" role="php" />
</dir>
</dir>
<dir name="doc" role="doc">
</dir>
<dir name="examples" role="doc">
<dir name="consumer" role="doc">
<file name="finish_auth.php" role="doc" />
<file name="common.php" role="doc" />
<file name="index.php" role="doc" />
<file name="try_auth.php" role="doc" />
</dir>
<dir name="server" role="doc">
<dir name="lib" role="doc">
<dir name="render" role="doc">
<file name="about.php" role="doc" />
<file name="login.php" role="doc" />
<file name="sites.php" role="doc" />
<file name="trust.php" role="doc" />
</dir>
<file name="actions.php" role="doc" />
<file name="common.php" role="doc" />
<file name="render.php" role="doc" />
<file name="session.php" role="doc" />
</dir>
<file name="openid-server.css" role="doc" />
<file name="index.php" role="doc" />
<file name="server.php" role="doc" />
<file name="setup.php" role="doc" />
</dir>
<file name="detect.php" role="doc" />
<file name="README" role="doc" />
</dir>
</dir>
</contents>
<dependencies>
<required>
<php>
<min>4.3.0</min>
</php>
<pearinstaller>
<min>1.4.5</min>
</pearinstaller>
</required>
<optional>
<package>
<name>PHPUnit</name>
<channel>pear.php.net</channel>
<min>1.1.1</min>
</package>
<package>
<name>PEAR_DB</name>
<channel>pear.php.net</channel>
<min>1.80</min>
</package>
<extension>
<name>pgsql</name>
</extension>
<extension>
<name>mysql</name>
</extension>
<extension>
<name>sqlite</name>
</extension>
<extension>
<name>bcmath</name>
</extension>
<extension>
<name>gmp</name>
</extension>
</optional>
</dependencies>
<!-- There really isn't much we should put in the phprelease tag,
although we should probably make a windows-specific release tag. -->
<phprelease/>
</package>

View file

@ -8,16 +8,22 @@ if (file_exists($probelang)) {
include dirname(__FILE__) . '/lang_en.inc.php';
}
function escape($message) {
return htmlspecialchars($message, ENT_QUOTES);
}
class serendipity_common_openid {
function redir_openidserver($openid_url, $store_path, $wfFlag=1) {
global $serendipity;
$path_extra = dirname(__FILE__).DIRECTORY_SEPARATOR.'PHP-openid';
$path_extra = dirname(__FILE__).DIRECTORY_SEPARATOR.'PHP-openid/';
$path = ini_get('include_path');
$path = $path_extra . PATH_SEPARATOR . $path;
ini_set('include_path', $path);
require_once("Auth/OpenID/Consumer.php");
require_once("Auth/OpenID/FileStore.php");
require_once "Auth/OpenID/Consumer.php";
require_once "Auth/OpenID/FileStore.php";
$store = new Auth_OpenID_FileStore($store_path);
$consumer = new Auth_OpenID_Consumer($store);
$trust_root = $serendipity['baseURL'];
@ -64,48 +70,99 @@ class serendipity_common_openid {
}
return false;
}
function authenticate_openid($getData, $store_path, $returnData = false) {
global $serendipity;
global $serendipity;
$trust_root = $serendipity['baseURL'] . 'serendipity_admin.php';
$path_extra = dirname(__FILE__).DIRECTORY_SEPARATOR.'PHP-openid';
$path = ini_get('include_path');
$path = $path_extra . PATH_SEPARATOR . $path;
ini_set('include_path', $path);
require_once("Auth/OpenID/Consumer.php");
require_once("Auth/OpenID/FileStore.php");
require_once("Auth/OpenID/SReg.php");
require_once("Auth/OpenID/PAPE.php");
$store = new Auth_OpenID_FileStore($store_path);
$consumer = new Auth_OpenID_Consumer($store);
$response = $consumer->complete($getData);
$response = $consumer->complete($trust_root); //, $getData);
if ($response->status == Auth_OpenID_CANCEL) {
$msg = 'Verification cancelled.';
$success = 'Verification cancelled.';
} else if ($response->status == Auth_OpenID_FAILURE) {
$msg = "OpenID authentication failed: " . $response->message;
$success = "OpenID authentication failed: " . $response->message;
} else if ($response->status == Auth_OpenID_SUCCESS) {
$openid = $response->identity_url;
$esc_identity = htmlspecialchars($openid, ENT_QUOTES);
$msg = sprintf('You have successfully verified ' .
// This means the authentication succeeded; extract the
// identity URL and Simple Registration data (if it was
// returned).
$openid = $response->getDisplayIdentifier();
$esc_identity = escape($openid);
$success = sprintf('You have successfully verified ' .
'<a href="%s">%s</a> as your identity.',
$esc_identity, $esc_identity);
if ($response->endpoint->canonicalID) {
$msg .= ' (XRI CanonicalID: '.$response->endpoint->canonicalID.') ';
$escaped_canonicalID = escape($response->endpoint->canonicalID);
$success .= ' (XRI CanonicalID: '.$escaped_canonicalID.') ';
}
$sreg = $response->extensionResponse('sreg');
$sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response);
$sreg = $sreg_resp->contents();
if (@$sreg['email']) {
$msg .= " You also returned '".$sreg['email']."' as your email.";
$email = $sreg['email'];
escape($sreg['email']);
$success .= " You also returned '".escape($sreg['email']).
"' as your email.";
}
if (@$sreg['nickname']) {
$success .= " Your nickname is '".escape($sreg['nickname']).
"'.";
}
if (@$sreg['fullname']) {
$msg .= " Your name is '".$sreg['fullname']."'";
$realname = $sreg['fullname'];
} else {
$realname = 'Anonymous';
$success .= " Your fullname is '".escape($sreg['fullname']).
"'.";
}
/*
$pape_resp = Auth_OpenID_PAPE_Response::fromSuccessResponse($response);
if ($pape_resp) {
if ($pape_resp->auth_policies) {
$success .= "<p>The following PAPE policies affected the authentication:</p><ul>";
foreach ($pape_resp->auth_policies as $uri) {
$escaped_uri = escape($uri);
$success .= "<li><tt>$escaped_uri</tt></li>";
}
$success .= "</ul>";
} else {
$success .= "<p>No PAPE policies affected the authentication.</p>";
}
if ($pape_resp->auth_age) {
$age = ($pape_resp->auth_age);
$success .= "<p>The authentication age returned by the " .
"server is: <tt>".$age."</tt></p>";
}
if ($pape_resp->nist_auth_level) {
$auth_level = escape($pape_resp->nist_auth_level);
$success .= "<p>The NIST auth level returned by the " .
"server is: <tt>".$auth_level."</tt></p>";
}
} else {
$success .= "<p>No PAPE response was sent by the provider.</p>";
}
*/
}
//print "Message: $success";
if (! empty($openid)) {
if ($returnData) {
return array('realname'=>$realname, 'email'=>$email, 'openID'=>$openid);

View file

@ -12,8 +12,8 @@ class serendipity_event_openid extends serendipity_event
$propbag->add('name', PLUGIN_OPENID_NAME);
$propbag->add('description', PLUGIN_OPENID_DESC);
$propbag->add('stackable', false);
$propbag->add('author', 'Rob Richards');
$propbag->add('version', '0.2');
$propbag->add('author', 'Grischa Brockhaus, Rob Richards');
$propbag->add('version', '0.3');
$propbag->add('requirements', array(
'serendipity' => '1.2',
'smarty' => '2.6.7',
@ -128,6 +128,7 @@ class serendipity_event_openid extends serendipity_event
}
} else if (! empty($serendipity['GET']['openidflag']) && ($serendipity['GET']['openidflag']==1)) {
$eventData = serendipity_common_openid::authenticate_openid($_GET, $this->get_config('storage_path'));
print_r($eventData);
} else if (! empty($serendipity['POST']['openid_url']) && ! empty($serendipity['POST']['action'])) {
$eventData = serendipity_common_openid::redir_openidserver($serendipity['POST']['openid_url'], $this->get_config('storage_path'), 1);
}