|
TYPO3 API
SVNRelease
|
00001 <?php 00002 00003 /** 00004 * The OpenID and Yadis discovery implementation for OpenID 1.2. 00005 */ 00006 00007 require_once "Auth/OpenID.php"; 00008 require_once "Auth/OpenID/Parse.php"; 00009 require_once "Auth/OpenID/Message.php"; 00010 require_once "Auth/Yadis/XRIRes.php"; 00011 require_once "Auth/Yadis/Yadis.php"; 00012 00013 // XML namespace value 00014 define('Auth_OpenID_XMLNS_1_0', 'http://openid.net/xmlns/1.0'); 00015 00016 // Yadis service types 00017 define('Auth_OpenID_TYPE_1_2', 'http://openid.net/signon/1.2'); 00018 define('Auth_OpenID_TYPE_1_1', 'http://openid.net/signon/1.1'); 00019 define('Auth_OpenID_TYPE_1_0', 'http://openid.net/signon/1.0'); 00020 define('Auth_OpenID_TYPE_2_0_IDP', 'http://specs.openid.net/auth/2.0/server'); 00021 define('Auth_OpenID_TYPE_2_0', 'http://specs.openid.net/auth/2.0/signon'); 00022 define('Auth_OpenID_RP_RETURN_TO_URL_TYPE', 00023 'http://specs.openid.net/auth/2.0/return_to'); 00024 00025 function Auth_OpenID_getOpenIDTypeURIs() 00026 { 00027 return array(Auth_OpenID_TYPE_2_0_IDP, 00028 Auth_OpenID_TYPE_2_0, 00029 Auth_OpenID_TYPE_1_2, 00030 Auth_OpenID_TYPE_1_1, 00031 Auth_OpenID_TYPE_1_0, 00032 Auth_OpenID_RP_RETURN_TO_URL_TYPE); 00033 } 00034 00035 /** 00036 * Object representing an OpenID service endpoint. 00037 */ 00038 class Auth_OpenID_ServiceEndpoint { 00039 function Auth_OpenID_ServiceEndpoint() 00040 { 00041 $this->claimed_id = null; 00042 $this->server_url = null; 00043 $this->type_uris = array(); 00044 $this->local_id = null; 00045 $this->canonicalID = null; 00046 $this->used_yadis = false; // whether this came from an XRDS 00047 $this->display_identifier = null; 00048 } 00049 00050 function getDisplayIdentifier() 00051 { 00052 if ($this->display_identifier) { 00053 return $this->display_identifier; 00054 } 00055 if (! $this->claimed_id) { 00056 return $this->claimed_id; 00057 } 00058 $parsed = parse_url($this->claimed_id); 00059 $scheme = $parsed['scheme']; 00060 $host = $parsed['host']; 00061 $path = $parsed['path']; 00062 if (array_key_exists('query', $parsed)) { 00063 $query = $parsed['query']; 00064 $no_frag = "$scheme://$host$path?$query"; 00065 } else { 00066 $no_frag = "$scheme://$host$path"; 00067 } 00068 return $no_frag; 00069 } 00070 00071 function usesExtension($extension_uri) 00072 { 00073 return in_array($extension_uri, $this->type_uris); 00074 } 00075 00076 function preferredNamespace() 00077 { 00078 if (in_array(Auth_OpenID_TYPE_2_0_IDP, $this->type_uris) || 00079 in_array(Auth_OpenID_TYPE_2_0, $this->type_uris)) { 00080 return Auth_OpenID_OPENID2_NS; 00081 } else { 00082 return Auth_OpenID_OPENID1_NS; 00083 } 00084 } 00085 00086 /* 00087 * Query this endpoint to see if it has any of the given type 00088 * URIs. This is useful for implementing other endpoint classes 00089 * that e.g. need to check for the presence of multiple versions 00090 * of a single protocol. 00091 * 00092 * @param $type_uris The URIs that you wish to check 00093 * 00094 * @return all types that are in both in type_uris and 00095 * $this->type_uris 00096 */ 00097 function matchTypes($type_uris) 00098 { 00099 $result = array(); 00100 foreach ($type_uris as $test_uri) { 00101 if ($this->supportsType($test_uri)) { 00102 $result[] = $test_uri; 00103 } 00104 } 00105 00106 return $result; 00107 } 00108 00109 function supportsType($type_uri) 00110 { 00111 // Does this endpoint support this type? 00112 return ((in_array($type_uri, $this->type_uris)) || 00113 (($type_uri == Auth_OpenID_TYPE_2_0) && 00114 $this->isOPIdentifier())); 00115 } 00116 00117 function compatibilityMode() 00118 { 00119 return $this->preferredNamespace() != Auth_OpenID_OPENID2_NS; 00120 } 00121 00122 function isOPIdentifier() 00123 { 00124 return in_array(Auth_OpenID_TYPE_2_0_IDP, $this->type_uris); 00125 } 00126 00127 function fromOPEndpointURL($op_endpoint_url) 00128 { 00129 // Construct an OP-Identifier OpenIDServiceEndpoint object for 00130 // a given OP Endpoint URL 00131 $obj = new Auth_OpenID_ServiceEndpoint(); 00132 $obj->server_url = $op_endpoint_url; 00133 $obj->type_uris = array(Auth_OpenID_TYPE_2_0_IDP); 00134 return $obj; 00135 } 00136 00137 function parseService($yadis_url, $uri, $type_uris, $service_element) 00138 { 00139 // Set the state of this object based on the contents of the 00140 // service element. Return true if successful, false if not 00141 // (if findOPLocalIdentifier returns false). 00142 $this->type_uris = $type_uris; 00143 $this->server_url = $uri; 00144 $this->used_yadis = true; 00145 00146 if (!$this->isOPIdentifier()) { 00147 $this->claimed_id = $yadis_url; 00148 $this->local_id = Auth_OpenID_findOPLocalIdentifier( 00149 $service_element, 00150 $this->type_uris); 00151 if ($this->local_id === false) { 00152 return false; 00153 } 00154 } 00155 00156 return true; 00157 } 00158 00159 function getLocalID() 00160 { 00161 // Return the identifier that should be sent as the 00162 // openid.identity_url parameter to the server. 00163 if ($this->local_id === null && $this->canonicalID === null) { 00164 return $this->claimed_id; 00165 } else { 00166 if ($this->local_id) { 00167 return $this->local_id; 00168 } else { 00169 return $this->canonicalID; 00170 } 00171 } 00172 } 00173 00174 /* 00175 * Parse the given document as XRDS looking for OpenID services. 00176 * 00177 * @return array of Auth_OpenID_ServiceEndpoint or null if the 00178 * document cannot be parsed. 00179 */ 00180 function fromXRDS($uri, $xrds_text) 00181 { 00182 $xrds =& Auth_Yadis_XRDS::parseXRDS($xrds_text); 00183 00184 if ($xrds) { 00185 $yadis_services = 00186 $xrds->services(array('filter_MatchesAnyOpenIDType')); 00187 return Auth_OpenID_makeOpenIDEndpoints($uri, $yadis_services); 00188 } 00189 00190 return null; 00191 } 00192 00193 /* 00194 * Create endpoints from a DiscoveryResult. 00195 * 00196 * @param discoveryResult Auth_Yadis_DiscoveryResult 00197 * @return array of Auth_OpenID_ServiceEndpoint or null if 00198 * endpoints cannot be created. 00199 */ 00200 function fromDiscoveryResult($discoveryResult) 00201 { 00202 if ($discoveryResult->isXRDS()) { 00203 return Auth_OpenID_ServiceEndpoint::fromXRDS( 00204 $discoveryResult->normalized_uri, 00205 $discoveryResult->response_text); 00206 } else { 00207 return Auth_OpenID_ServiceEndpoint::fromHTML( 00208 $discoveryResult->normalized_uri, 00209 $discoveryResult->response_text); 00210 } 00211 } 00212 00213 function fromHTML($uri, $html) 00214 { 00215 $discovery_types = array( 00216 array(Auth_OpenID_TYPE_2_0, 00217 'openid2.provider', 'openid2.local_id'), 00218 array(Auth_OpenID_TYPE_1_1, 00219 'openid.server', 'openid.delegate') 00220 ); 00221 00222 $services = array(); 00223 00224 foreach ($discovery_types as $triple) { 00225 list($type_uri, $server_rel, $delegate_rel) = $triple; 00226 00227 $urls = Auth_OpenID_legacy_discover($html, $server_rel, 00228 $delegate_rel); 00229 00230 if ($urls === false) { 00231 continue; 00232 } 00233 00234 list($delegate_url, $server_url) = $urls; 00235 00236 $service = new Auth_OpenID_ServiceEndpoint(); 00237 $service->claimed_id = $uri; 00238 $service->local_id = $delegate_url; 00239 $service->server_url = $server_url; 00240 $service->type_uris = array($type_uri); 00241 00242 $services[] = $service; 00243 } 00244 00245 return $services; 00246 } 00247 00248 function copy() 00249 { 00250 $x = new Auth_OpenID_ServiceEndpoint(); 00251 00252 $x->claimed_id = $this->claimed_id; 00253 $x->server_url = $this->server_url; 00254 $x->type_uris = $this->type_uris; 00255 $x->local_id = $this->local_id; 00256 $x->canonicalID = $this->canonicalID; 00257 $x->used_yadis = $this->used_yadis; 00258 00259 return $x; 00260 } 00261 } 00262 00263 function Auth_OpenID_findOPLocalIdentifier($service, $type_uris) 00264 { 00265 // Extract a openid:Delegate value from a Yadis Service element. 00266 // If no delegate is found, returns null. Returns false on 00267 // discovery failure (when multiple delegate/localID tags have 00268 // different values). 00269 00270 $service->parser->registerNamespace('openid', 00271 Auth_OpenID_XMLNS_1_0); 00272 00273 $service->parser->registerNamespace('xrd', 00274 Auth_Yadis_XMLNS_XRD_2_0); 00275 00276 $parser =& $service->parser; 00277 00278 $permitted_tags = array(); 00279 00280 if (in_array(Auth_OpenID_TYPE_1_1, $type_uris) || 00281 in_array(Auth_OpenID_TYPE_1_0, $type_uris)) { 00282 $permitted_tags[] = 'openid:Delegate'; 00283 } 00284 00285 if (in_array(Auth_OpenID_TYPE_2_0, $type_uris)) { 00286 $permitted_tags[] = 'xrd:LocalID'; 00287 } 00288 00289 $local_id = null; 00290 00291 foreach ($permitted_tags as $tag_name) { 00292 $tags = $service->getElements($tag_name); 00293 00294 foreach ($tags as $tag) { 00295 $content = $parser->content($tag); 00296 00297 if ($local_id === null) { 00298 $local_id = $content; 00299 } else if ($local_id != $content) { 00300 return false; 00301 } 00302 } 00303 } 00304 00305 return $local_id; 00306 } 00307 00308 function filter_MatchesAnyOpenIDType($service) 00309 { 00310 $uris = $service->getTypes(); 00311 00312 foreach ($uris as $uri) { 00313 if (in_array($uri, Auth_OpenID_getOpenIDTypeURIs())) { 00314 return true; 00315 } 00316 } 00317 00318 return false; 00319 } 00320 00321 function Auth_OpenID_bestMatchingService($service, $preferred_types) 00322 { 00323 // Return the index of the first matching type, or something 00324 // higher if no type matches. 00325 // 00326 // This provides an ordering in which service elements that 00327 // contain a type that comes earlier in the preferred types list 00328 // come before service elements that come later. If a service 00329 // element has more than one type, the most preferred one wins. 00330 00331 foreach ($preferred_types as $index => $typ) { 00332 if (in_array($typ, $service->type_uris)) { 00333 return $index; 00334 } 00335 } 00336 00337 return count($preferred_types); 00338 } 00339 00340 function Auth_OpenID_arrangeByType($service_list, $preferred_types) 00341 { 00342 // Rearrange service_list in a new list so services are ordered by 00343 // types listed in preferred_types. Return the new list. 00344 00345 // Build a list with the service elements in tuples whose 00346 // comparison will prefer the one with the best matching service 00347 $prio_services = array(); 00348 foreach ($service_list as $index => $service) { 00349 $prio_services[] = array(Auth_OpenID_bestMatchingService($service, 00350 $preferred_types), 00351 $index, $service); 00352 } 00353 00354 sort($prio_services); 00355 00356 // Now that the services are sorted by priority, remove the sort 00357 // keys from the list. 00358 foreach ($prio_services as $index => $s) { 00359 $prio_services[$index] = $prio_services[$index][2]; 00360 } 00361 00362 return $prio_services; 00363 } 00364 00365 // Extract OP Identifier services. If none found, return the rest, 00366 // sorted with most preferred first according to 00367 // OpenIDServiceEndpoint.openid_type_uris. 00368 // 00369 // openid_services is a list of OpenIDServiceEndpoint objects. 00370 // 00371 // Returns a list of OpenIDServiceEndpoint objects.""" 00372 function Auth_OpenID_getOPOrUserServices($openid_services) 00373 { 00374 $op_services = Auth_OpenID_arrangeByType($openid_services, 00375 array(Auth_OpenID_TYPE_2_0_IDP)); 00376 00377 $openid_services = Auth_OpenID_arrangeByType($openid_services, 00378 Auth_OpenID_getOpenIDTypeURIs()); 00379 00380 if ($op_services) { 00381 return $op_services; 00382 } else { 00383 return $openid_services; 00384 } 00385 } 00386 00387 function Auth_OpenID_makeOpenIDEndpoints($uri, $yadis_services) 00388 { 00389 $s = array(); 00390 00391 if (!$yadis_services) { 00392 return $s; 00393 } 00394 00395 foreach ($yadis_services as $service) { 00396 $type_uris = $service->getTypes(); 00397 $uris = $service->getURIs(); 00398 00399 // If any Type URIs match and there is an endpoint URI 00400 // specified, then this is an OpenID endpoint 00401 if ($type_uris && 00402 $uris) { 00403 foreach ($uris as $service_uri) { 00404 $openid_endpoint = new Auth_OpenID_ServiceEndpoint(); 00405 if ($openid_endpoint->parseService($uri, 00406 $service_uri, 00407 $type_uris, 00408 $service)) { 00409 $s[] = $openid_endpoint; 00410 } 00411 } 00412 } 00413 } 00414 00415 return $s; 00416 } 00417 00418 function Auth_OpenID_discoverWithYadis($uri, $fetcher, 00419 $endpoint_filter='Auth_OpenID_getOPOrUserServices', 00420 $discover_function=null) 00421 { 00422 // Discover OpenID services for a URI. Tries Yadis and falls back 00423 // on old-style <link rel='...'> discovery if Yadis fails. 00424 00425 // Might raise a yadis.discover.DiscoveryFailure if no document 00426 // came back for that URI at all. I don't think falling back to 00427 // OpenID 1.0 discovery on the same URL will help, so don't bother 00428 // to catch it. 00429 if ($discover_function === null) { 00430 $discover_function = array('Auth_Yadis_Yadis', 'discover'); 00431 } 00432 00433 $openid_services = array(); 00434 00435 $response = call_user_func_array($discover_function, 00436 array($uri, $fetcher)); 00437 00438 $yadis_url = $response->normalized_uri; 00439 $yadis_services = array(); 00440 00441 if ($response->isFailure()) { 00442 return array($uri, array()); 00443 } 00444 00445 $openid_services = Auth_OpenID_ServiceEndpoint::fromXRDS( 00446 $yadis_url, 00447 $response->response_text); 00448 00449 if (!$openid_services) { 00450 if ($response->isXRDS()) { 00451 return Auth_OpenID_discoverWithoutYadis($uri, 00452 $fetcher); 00453 } 00454 00455 // Try to parse the response as HTML to get OpenID 1.0/1.1 00456 // <link rel="..."> 00457 $openid_services = Auth_OpenID_ServiceEndpoint::fromHTML( 00458 $yadis_url, 00459 $response->response_text); 00460 } 00461 00462 $openid_services = call_user_func_array($endpoint_filter, 00463 array($openid_services)); 00464 00465 return array($yadis_url, $openid_services); 00466 } 00467 00468 function Auth_OpenID_discoverURI($uri, $fetcher) 00469 { 00470 $uri = Auth_OpenID::normalizeUrl($uri); 00471 return Auth_OpenID_discoverWithYadis($uri, $fetcher); 00472 } 00473 00474 function Auth_OpenID_discoverWithoutYadis($uri, $fetcher) 00475 { 00476 $http_resp = @$fetcher->get($uri); 00477 00478 if ($http_resp->status != 200 and $http_resp->status != 206) { 00479 return array($uri, array()); 00480 } 00481 00482 $identity_url = $http_resp->final_url; 00483 00484 // Try to parse the response as HTML to get OpenID 1.0/1.1 <link 00485 // rel="..."> 00486 $openid_services = Auth_OpenID_ServiceEndpoint::fromHTML( 00487 $identity_url, 00488 $http_resp->body); 00489 00490 return array($identity_url, $openid_services); 00491 } 00492 00493 function Auth_OpenID_discoverXRI($iname, $fetcher) 00494 { 00495 $resolver = new Auth_Yadis_ProxyResolver($fetcher); 00496 list($canonicalID, $yadis_services) = 00497 $resolver->query($iname, 00498 Auth_OpenID_getOpenIDTypeURIs(), 00499 array('filter_MatchesAnyOpenIDType')); 00500 00501 $openid_services = Auth_OpenID_makeOpenIDEndpoints($iname, 00502 $yadis_services); 00503 00504 $openid_services = Auth_OpenID_getOPOrUserServices($openid_services); 00505 00506 for ($i = 0; $i < count($openid_services); $i++) { 00507 $openid_services[$i]->canonicalID = $canonicalID; 00508 $openid_services[$i]->claimed_id = $canonicalID; 00509 $openid_services[$i]->display_identifier = $iname; 00510 } 00511 00512 // FIXME: returned xri should probably be in some normal form 00513 return array($iname, $openid_services); 00514 } 00515 00516 function Auth_OpenID_discover($uri, $fetcher) 00517 { 00518 // If the fetcher (i.e., PHP) doesn't support SSL, we can't do 00519 // discovery on an HTTPS URL. 00520 if ($fetcher->isHTTPS($uri) && !$fetcher->supportsSSL()) { 00521 return array($uri, array()); 00522 } 00523 00524 if (Auth_Yadis_identifierScheme($uri) == 'XRI') { 00525 $result = Auth_OpenID_discoverXRI($uri, $fetcher); 00526 } else { 00527 $result = Auth_OpenID_discoverURI($uri, $fetcher); 00528 } 00529 00530 // If the fetcher doesn't support SSL, we can't interact with 00531 // HTTPS server URLs; remove those endpoints from the list. 00532 if (!$fetcher->supportsSSL()) { 00533 $http_endpoints = array(); 00534 list($new_uri, $endpoints) = $result; 00535 00536 foreach ($endpoints as $e) { 00537 if (!$fetcher->isHTTPS($e->server_url)) { 00538 $http_endpoints[] = $e; 00539 } 00540 } 00541 00542 $result = array($new_uri, $http_endpoints); 00543 } 00544 00545 return $result; 00546 } 00547 00548 ?>
1.8.0