import v1.1.0_beta1 | 2009-08-21
This commit is contained in:
529
libs/Auth/Yadis/Manager.php
Normal file
529
libs/Auth/Yadis/Manager.php
Normal file
@ -0,0 +1,529 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Yadis service manager to be used during yadis-driven authentication
|
||||
* attempts.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 OpenID
|
||||
*/
|
||||
class Auth_Yadis_PHPSession {
|
||||
/**
|
||||
* Set a session key/value pair.
|
||||
*
|
||||
* @param string $name The name of the session key to add.
|
||||
* @param string $value The value to add to the session.
|
||||
*/
|
||||
function set($name, $value)
|
||||
{
|
||||
$_SESSION[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a key's value from the session.
|
||||
*
|
||||
* @param string $name The name of the key to retrieve.
|
||||
* @param string $default The optional value to return if the key
|
||||
* is not found in the session.
|
||||
* @return string $result The key's value in the session or
|
||||
* $default if it isn't found.
|
||||
*/
|
||||
function get($name, $default=null)
|
||||
{
|
||||
if (array_key_exists($name, $_SESSION)) {
|
||||
return $_SESSION[$name];
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a key/value pair from the session.
|
||||
*
|
||||
* @param string $name The name of the key to remove.
|
||||
*/
|
||||
function del($name)
|
||||
{
|
||||
unset($_SESSION[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the contents of the session in array form.
|
||||
*/
|
||||
function contents()
|
||||
{
|
||||
return $_SESSION;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A session helper class designed to translate between arrays and
|
||||
* objects. Note that the class used must have a constructor that
|
||||
* takes no parameters. This is not a general solution, but it works
|
||||
* 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 Auth_Yadis_SessionLoader {
|
||||
/**
|
||||
* Override this.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
function check($data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a session data value (an array), this creates an object
|
||||
* (returned by $this->newObject()) whose attributes and values
|
||||
* are those in $data. Returns null if $data lacks keys found in
|
||||
* $this->requiredKeys(). Returns null if $this->check($data)
|
||||
* evaluates to false. Returns null if $this->newObject()
|
||||
* evaluates to false.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
function fromSession($data)
|
||||
{
|
||||
if (!$data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$required = $this->requiredKeys();
|
||||
|
||||
foreach ($required as $k) {
|
||||
if (!array_key_exists($k, $data)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->check($data)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = array_merge($data, $this->prepareForLoad($data));
|
||||
$obj = $this->newObject($data);
|
||||
|
||||
if (!$obj) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($required as $k) {
|
||||
$obj->$k = $data[$k];
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new instance of this loader's class, using the
|
||||
* 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)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of keys and values built from the attributes
|
||||
* 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)
|
||||
{
|
||||
$data = array();
|
||||
foreach ($obj as $k => $v) {
|
||||
$data[$k] = $v;
|
||||
}
|
||||
|
||||
$extra = $this->prepareForSave($obj);
|
||||
|
||||
if ($extra && is_array($extra)) {
|
||||
foreach ($extra as $k => $v) {
|
||||
$data[$k] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
function prepareForSave($obj)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
function requiredKeys()
|
||||
{
|
||||
$obj = new Auth_OpenID_ServiceEndpoint();
|
||||
$data = array();
|
||||
foreach ($obj as $k => $v) {
|
||||
$data[] = $k;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
function check($data)
|
||||
{
|
||||
return is_array($data['type_uris']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A concrete loader implementation for Auth_Yadis_Managers.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
class Auth_Yadis_ManagerLoader extends Auth_Yadis_SessionLoader {
|
||||
function requiredKeys()
|
||||
{
|
||||
return array('starting_url',
|
||||
'yadis_url',
|
||||
'services',
|
||||
'session_key',
|
||||
'_current',
|
||||
'stale');
|
||||
}
|
||||
|
||||
function newObject($data)
|
||||
{
|
||||
return new Auth_Yadis_Manager($data['starting_url'],
|
||||
$data['yadis_url'],
|
||||
$data['services'],
|
||||
$data['session_key']);
|
||||
}
|
||||
|
||||
function check($data)
|
||||
{
|
||||
return is_array($data['services']);
|
||||
}
|
||||
|
||||
function prepareForLoad($data)
|
||||
{
|
||||
$loader = new Auth_OpenID_ServiceEndpointLoader();
|
||||
$services = array();
|
||||
foreach ($data['services'] as $s) {
|
||||
$services[] = $loader->fromSession($s);
|
||||
}
|
||||
return array('services' => $services);
|
||||
}
|
||||
|
||||
function prepareForSave($obj)
|
||||
{
|
||||
$loader = new Auth_OpenID_ServiceEndpointLoader();
|
||||
$services = array();
|
||||
foreach ($obj->services as $s) {
|
||||
$services[] = $loader->toSession($s);
|
||||
}
|
||||
return array('services' => $services);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Yadis service manager which stores state in a session and
|
||||
* iterates over <Service> elements in a Yadis XRDS document and lets
|
||||
* a caller attempt to use each one. This is used by the Yadis
|
||||
* library internally.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
class Auth_Yadis_Manager {
|
||||
|
||||
/**
|
||||
* Intialize a new yadis service manager.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
function Auth_Yadis_Manager($starting_url, $yadis_url,
|
||||
$services, $session_key)
|
||||
{
|
||||
// The URL that was used to initiate the Yadis protocol
|
||||
$this->starting_url = $starting_url;
|
||||
|
||||
// The URL after following redirects (the identifier)
|
||||
$this->yadis_url = $yadis_url;
|
||||
|
||||
// List of service elements
|
||||
$this->services = $services;
|
||||
|
||||
$this->session_key = $session_key;
|
||||
|
||||
// Reference to the current service object
|
||||
$this->_current = null;
|
||||
|
||||
// Stale flag for cleanup if PHP lib has trouble.
|
||||
$this->stale = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
function length()
|
||||
{
|
||||
// How many untried services remain?
|
||||
return count($this->services);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next service
|
||||
*
|
||||
* $this->current() will continue to return that service until the
|
||||
* next call to this method.
|
||||
*/
|
||||
function nextService()
|
||||
{
|
||||
|
||||
if ($this->services) {
|
||||
$this->_current = array_shift($this->services);
|
||||
} else {
|
||||
$this->_current = null;
|
||||
}
|
||||
|
||||
return $this->_current;
|
||||
}
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
function current()
|
||||
{
|
||||
// Return the current service.
|
||||
// Returns None if there are no services left.
|
||||
return $this->_current;
|
||||
}
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
function forURL($url)
|
||||
{
|
||||
return in_array($url, array($this->starting_url, $this->yadis_url));
|
||||
}
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
function started()
|
||||
{
|
||||
// Has the first service been returned?
|
||||
return $this->_current !== null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* State management for discovery.
|
||||
*
|
||||
* 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 .cleanup() to clean up the
|
||||
* session state.
|
||||
*
|
||||
* @package OpenID
|
||||
*/
|
||||
class Auth_Yadis_Discovery {
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
var $DEFAULT_SUFFIX = 'auth';
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
var $PREFIX = '_yadis_services_';
|
||||
|
||||
/**
|
||||
* Initialize a discovery object.
|
||||
*
|
||||
* @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 Auth_Yadis_Discovery(&$session, $url,
|
||||
$session_key_suffix = null)
|
||||
{
|
||||
/// Initialize a discovery object
|
||||
$this->session =& $session;
|
||||
$this->url = $url;
|
||||
if ($session_key_suffix === null) {
|
||||
$session_key_suffix = $this->DEFAULT_SUFFIX;
|
||||
}
|
||||
|
||||
$this->session_key_suffix = $session_key_suffix;
|
||||
$this->session_key = $this->PREFIX . $this->session_key_suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the next authentication service for the pair of
|
||||
* user_input and session. This function handles fallback.
|
||||
*/
|
||||
function getNextService($discover_cb, &$fetcher)
|
||||
{
|
||||
$manager = $this->getManager();
|
||||
if (!$manager || (!$manager->services)) {
|
||||
$this->destroyManager();
|
||||
|
||||
list($yadis_url, $services) = call_user_func($discover_cb,
|
||||
$this->url,
|
||||
$fetcher);
|
||||
|
||||
$manager = $this->createManager($services, $yadis_url);
|
||||
}
|
||||
|
||||
if ($manager) {
|
||||
$loader = new Auth_Yadis_ManagerLoader();
|
||||
$service = $manager->nextService();
|
||||
$this->session->set($this->session_key,
|
||||
serialize($loader->toSession($manager)));
|
||||
} else {
|
||||
$service = null;
|
||||
}
|
||||
|
||||
return $service;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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($force=false)
|
||||
{
|
||||
$manager = $this->getManager($force);
|
||||
if ($manager) {
|
||||
$service = $manager->current();
|
||||
$this->destroyManager($force);
|
||||
} else {
|
||||
$service = null;
|
||||
}
|
||||
|
||||
return $service;
|
||||
}
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
function getSessionKey()
|
||||
{
|
||||
// Get the session key for this starting URL and suffix
|
||||
return $this->PREFIX . $this->session_key_suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*
|
||||
* @param $force True if the manager should be returned regardless
|
||||
* of whether it's a manager for $this->url.
|
||||
*/
|
||||
function &getManager($force=false)
|
||||
{
|
||||
// Extract the YadisServiceManager for this object's URL and
|
||||
// suffix from the session.
|
||||
|
||||
$manager_str = $this->session->get($this->getSessionKey());
|
||||
$manager = null;
|
||||
|
||||
if ($manager_str !== null) {
|
||||
$loader = new Auth_Yadis_ManagerLoader();
|
||||
$manager = $loader->fromSession(unserialize($manager_str));
|
||||
}
|
||||
|
||||
if ($manager && ($manager->forURL($this->url) || $force)) {
|
||||
return $manager;
|
||||
} else {
|
||||
$unused = null;
|
||||
return $unused;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @access private
|
||||
*/
|
||||
function &createManager($services, $yadis_url = null)
|
||||
{
|
||||
$key = $this->getSessionKey();
|
||||
if ($this->getManager()) {
|
||||
return $this->getManager();
|
||||
}
|
||||
|
||||
if ($services) {
|
||||
$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($force=false)
|
||||
{
|
||||
if ($this->getManager($force) !== null) {
|
||||
$key = $this->getSessionKey();
|
||||
$this->session->del($key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
Reference in New Issue
Block a user