|
<?php |
|
|
|
/** |
|
* The OpenID and Yadis discovery implementation for OpenID 1.2. |
|
*/ |
|
|
|
require_once "Auth/OpenID.php"; |
|
require_once "Auth/OpenID/Parse.php"; |
|
require_once "Auth/OpenID/Message.php"; |
|
require_once "Auth/Yadis/XRIRes.php"; |
|
require_once "Auth/Yadis/Yadis.php"; |
|
|
|
// 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. |
|
*/ |
|
class Auth_OpenID_ServiceEndpoint { |
|
function Auth_OpenID_ServiceEndpoint() |
|
{ |
|
$this->claimed_id = null; |
|
$this->server_url = null; |
|
$this->type_uris = array(); |
|
$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) |
|
{ |
|
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. Return true if successful, false if not |
|
// (if findOPLocalIdentifier returns false). |
|
$this->type_uris = $type_uris; |
|
$this->server_url = $uri; |
|
$this->used_yadis = true; |
|
|
|
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 getLocalID() |
|
{ |
|
// Return the identifier that should be sent as the |
|
// openid.identity_url parameter to the server. |
|
if ($this->local_id === null && $this->canonicalID === null) { |
|
return $this->claimed_id; |
|
} else { |
|
if ($this->local_id) { |
|
return $this->local_id; |
|
} else { |
|
return $this->canonicalID; |
|
} |
|
} |
|
} |
|
|
|
/* |
|
* 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) |
|
{ |
|
$xrds =& Auth_Yadis_XRDS::parseXRDS($xrds_text); |
|
|
|
if ($xrds) { |
|
$yadis_services = |
|
$xrds->services(array('filter_MatchesAnyOpenIDConsumerType')); |
|
return Auth_OpenID_makeOpenIDEndpoints($uri, $yadis_services); |
|
} |
|
|
|
return null; |
|
} |
|
|
|
/* |
|
* 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 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, Auth_OpenID_getOpenIDTypeURIs())) { |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
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. |
|
// |
|