123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607 |
- <?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.
- //
- // 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 (!$yadis_services) {
- return $s;
- }
- foreach ($yadis_services as $service) {
- $type_uris = $service->getTypes();
- $uris = $service->getURIs();
- // If any Type URIs match and there is an endpoint URI
- // specified, then this is an OpenID endpoint
- if ($type_uris &&
- $uris) {
- foreach ($uris as $service_uri) {
- $openid_endpoint = new Auth_OpenID_ServiceEndpoint();
- if ($openid_endpoint->parseService($uri,
- $service_uri,
- $type_uris,
- $service)) {
- $s[] = $openid_endpoint;
- }
- }
- }
- }
- return $s;
- }
- 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.
- // Might raise a yadis.discover.DiscoveryFailure if no document
- // 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();
- $response = call_user_func_array($discover_function,
- array($uri, $fetcher));
- $yadis_url = $response->normalized_uri;
- $yadis_services = array();
- if ($response->isFailure() && !$response->isXRDS()) {
- return array($uri, array());
- }
- $openid_services = Auth_OpenID_ServiceEndpoint::fromXRDS(
- $yadis_url,
- $response->response_text);
- if (!$openid_services) {
- 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="...">
- $openid_services = Auth_OpenID_ServiceEndpoint::fromHTML(
- $yadis_url,
- $response->response_text);
- }
- $openid_services = call_user_func_array($endpoint_filter,
- array($openid_services));
- return array($yadis_url, $openid_services);
- }
- function Auth_OpenID_discoverURI($uri, $fetcher)
- {
- $uri = Auth_OpenID::normalizeUrl($uri);
- return Auth_OpenID_discoverWithYadis($uri, $fetcher);
- }
- function Auth_OpenID_discoverWithoutYadis($uri, $fetcher)
- {
- $http_resp = @$fetcher->get($uri);
- 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="...">
- $openid_services = Auth_OpenID_ServiceEndpoint::fromHTML(
- $identity_url,
- $http_resp->body);
- return array($identity_url, $openid_services);
- }
- function Auth_OpenID_discoverXRI($iname, $fetcher)
- {
- $resolver = new Auth_Yadis_ProxyResolver($fetcher);
- list($canonicalID, $yadis_services) =
- $resolver->query($iname,
- Auth_OpenID_getOpenIDTypeURIs(),
- array('filter_MatchesAnyOpenIDType'));
- $openid_services = Auth_OpenID_makeOpenIDEndpoints($iname,
- $yadis_services);
- $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, $openid_services);
- }
- function Auth_OpenID_discover($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;
- }
|