summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortailor <cygnus@janrain.com>2006-08-17 23:02:48 +0000
committertailor <cygnus@janrain.com>2006-08-17 23:02:48 +0000
commit7365396152cab936a2e6b270fa94a2a52d8e0621 (patch)
tree810ab96e33ce129e8e4ce8839ef80a8356768a6b
parentddcf13c181bd5bf17a37c93dd87e32ee5bf56f82 (diff)
downloadphp-openid-7365396152cab936a2e6b270fa94a2a52d8e0621.zip
php-openid-7365396152cab936a2e6b270fa94a2a52d8e0621.tar.gz
php-openid-7365396152cab936a2e6b270fa94a2a52d8e0621.tar.bz2
[project @ Added more changes from python port (root authority and canonical ID checking)]
-rw-r--r--Services/Yadis/XRDS.php9
-rw-r--r--Services/Yadis/XRI.php132
-rw-r--r--Services/Yadis/XRIRes.php65
-rw-r--r--Tests/Services/Yadis/XRDS.php42
-rw-r--r--Tests/Services/Yadis/XRI.php34
5 files changed, 229 insertions, 53 deletions
diff --git a/Services/Yadis/XRDS.php b/Services/Yadis/XRDS.php
index 55674c4..2d3a9bc 100644
--- a/Services/Yadis/XRDS.php
+++ b/Services/Yadis/XRDS.php
@@ -196,10 +196,11 @@ class Services_Yadis_XRDS {
* Instantiate a Services_Yadis_XRDS object. Requires an XPath
* instance which has been used to parse a valid XRDS document.
*/
- function Services_Yadis_XRDS(&$xmlParser, &$xrdNode)
+ function Services_Yadis_XRDS(&$xmlParser, &$xrdNodes)
{
$this->parser =& $xmlParser;
- $this->xrdNode = $xrdNode;
+ $this->xrdNode = $xrdNodes[count($xrdNodes) - 1];
+ $this->allXrdNodes =& $xrdNodes;
$this->serviceList = array();
$this->_parse();
}
@@ -255,13 +256,13 @@ class Services_Yadis_XRDS {
}
// Get the last XRD node.
- $xrd_nodes = $parser->evalXPath('/xrds:XRDS[1]/xrd:XRD[last()]');
+ $xrd_nodes = $parser->evalXPath('/xrds:XRDS[1]/xrd:XRD');
if (!$xrd_nodes) {
return null;
}
- $xrds = new Services_Yadis_XRDS($parser, $xrd_nodes[0]);
+ $xrds = new Services_Yadis_XRDS($parser, $xrd_nodes);
return $xrds;
}
diff --git a/Services/Yadis/XRI.php b/Services/Yadis/XRI.php
index c4b49ba..df943be 100644
--- a/Services/Yadis/XRI.php
+++ b/Services/Yadis/XRI.php
@@ -11,7 +11,6 @@
require_once 'Services/Yadis/Misc.php';
require_once 'Services/Yadis/Yadis.php';
-require_once 'Services/Yadis/XRDS.php';
require_once 'Auth/OpenID.php';
$DEFAULT_PROXY = 'http://proxy.xri.net/';
@@ -82,54 +81,6 @@ function Services_Yadis_iriToURI($iri)
}
}
-class Services_Yadis_ProxyResolver {
- function Services_Yadis_ProxyResolver(&$fetcher, $proxy_url = null)
- {
- global $DEFAULT_PROXY;
-
- $this->fetcher =& $fetcher;
- $this->proxy_url = $proxy_url;
- if (!$this->proxy_url) {
- $this->proxy_url = $DEFAULT_PROXY;
- }
- }
-
- function queryURL($xri, $service_type)
- {
- // trim off the xri:// prefix
- $qxri = substr(Services_Yadis_toURINormal($xri), 6);
- $hxri = $this->proxy_url . $qxri;
- $args = array(
- '_xrd_r' => 'application/xrds+xml',
- '_xrd_t' => $service_type
- );
- $query = Services_Yadis_XRIAppendArgs($hxri, $args);
- return $query;
- }
-
- function query($xri, $service_types, $filters = array())
- {
- $services = array();
- foreach ($service_types as $service_type) {
- $url = $this->queryURL($xri, $service_type);
- $response = $this->fetcher->get($url);
- if ($response->status != 200) {
- continue;
- }
- $xrds = Services_Yadis_XRDS::parseXRDS($response->body);
- if (!$xrds) {
- continue;
- }
- $some_services = $xrds->services($filters);
- $services = array_merge($services, $some_services);
- // TODO:
- // * If we do get hits for multiple service_types, we're
- // almost certainly going to have duplicated service
- // entries and broken priority ordering.
- }
- return $services;
- }
-}
function Services_Yadis_XRIAppendArgs($url, $args)
{
@@ -171,4 +122,87 @@ function Services_Yadis_XRIAppendArgs($url, $args)
return $url . $sep . Auth_OpenID::httpBuildQuery($args);
}
+function Services_Yadis_providerIsAuthoritative($providerID, $canonicalID)
+{
+ $lastbang = strrpos($canonicalID, '!');
+ $p = substr($canonicalID, 0, $lastbang);
+ return $p == $providerID;
+}
+
+function Services_Yadis_rootAuthority($xri)
+{
+ global $XRI_AUTHORITIES;
+
+ // Return the root authority for an XRI.
+
+ $authority = explode('/', $xri, 2);
+ $authority = $authority[0];
+ if ($authority[0] == '(') {
+ // Cross-reference.
+ // XXX: This is incorrect if someone nests cross-references so
+ // there is another close-paren in there. Hopefully nobody
+ // does that before we have a real xriparse function.
+ // Hopefully nobody does that *ever*.
+ return substr($authority, 0, strpos($authority, ')') + 1);
+ } else if (in_array($authority[0], $XRI_AUTHORITIES)) {
+ // Other XRI reference.
+ return $authority[0];
+ } else {
+ // IRI reference.
+ $_segments = explode("!", $authority);
+ $segments = array();
+ foreach ($_segments as $s) {
+ $segments = array_merge($segments, explode("*", $s));
+ }
+ return $segments[0];
+ }
+}
+
+function Services_Yadis_getCanonicalID($iname, $xrds)
+{
+ // Returns FALSE or a canonical ID value.
+
+ // Now nodes are in reverse order.
+ $xrd_list = array_reverse($xrds->allXrdNodes);
+ $parser =& $xrds->parser;
+ $node = $xrd_list[0];
+
+ $canonicalID_nodes = $parser->evalXPath('xrd:CanonicalID', $node);
+
+ if (!$canonicalID_nodes) {
+ return false;
+ }
+
+ $canonicalID = $canonicalID_nodes[count($canonicalID_nodes) - 1];
+ $canonicalID = $parser->content($canonicalID);
+
+ $childID = $canonicalID;
+
+ for ($i = 1; $i < count($xrd_list); $i++) {
+ $xrd = $xrd_list[$i];
+
+ $parent_sought = substr($childID, 0, strrpos($childID, '!'));
+ $parent_list = array();
+
+ foreach ($parser->evalXPath('xrd:CanonicalID', $xrd) as $c) {
+ $parent_list[] = $parser->content($c);
+ }
+
+ if (!in_array($parent_sought, $parent_list)) {
+ // raise XRDSFraud.
+ return false;
+ }
+
+ $childID = $parent_sought;
+ }
+
+ $root = Services_Yadis_rootAuthority($iname);
+ if (!Services_Yadis_providerIsAuthoritative($root, $childID)) {
+ // raise XRDSFraud.
+ return false;
+ }
+
+ return $canonicalID;
+}
+
?> \ No newline at end of file
diff --git a/Services/Yadis/XRIRes.php b/Services/Yadis/XRIRes.php
new file mode 100644
index 0000000..106aaeb
--- /dev/null
+++ b/Services/Yadis/XRIRes.php
@@ -0,0 +1,65 @@
+<?php
+
+require_once 'Services/Yadis/XRDS.php';
+require_once 'Services/Yadis/XRI.php';
+
+class Services_Yadis_ProxyResolver {
+ function Services_Yadis_ProxyResolver(&$fetcher, $proxy_url = null)
+ {
+ global $DEFAULT_PROXY;
+
+ $this->fetcher =& $fetcher;
+ $this->proxy_url = $proxy_url;
+ if (!$this->proxy_url) {
+ $this->proxy_url = $DEFAULT_PROXY;
+ }
+ }
+
+ function queryURL($xri, $service_type = null)
+ {
+ // trim off the xri:// prefix
+ $qxri = substr(Services_Yadis_toURINormal($xri), 6);
+ $hxri = $this->proxy_url . $qxri;
+ $args = array(
+ '_xrd_r' => 'application/xrds+xml'
+ );
+
+ if ($service_type) {
+ $args['_xrd_t'] = $service_type;
+ } else {
+ // Don't perform service endpoint selection.
+ $args['_xrd_r'] .= ';sep=false';
+ }
+
+ $query = Services_Yadis_XRIAppendArgs($hxri, $args);
+ return $query;
+ }
+
+ function query($xri, $service_types, $filters = array())
+ {
+ $services = array();
+ $canonicalID = null;
+ foreach ($service_types as $service_type) {
+ $url = $this->queryURL($xri, $service_type);
+ $response = $this->fetcher->get($url);
+ if ($response->status != 200) {
+ continue;
+ }
+ $xrds = Services_Yadis_XRDS::parseXRDS($response->body);
+ if (!$xrds) {
+ continue;
+ }
+ $canonicalID = Services_Yadis_getCanonicalID($xri,
+ $xrds->allXrdNodes);
+ $some_services = $xrds->services($filters);
+ $services = array_merge($services, $some_services);
+ // TODO:
+ // * If we do get hits for multiple service_types, we're
+ // almost certainly going to have duplicated service
+ // entries and broken priority ordering.
+ }
+ return array($canonicalID, $services);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/Tests/Services/Yadis/XRDS.php b/Tests/Services/Yadis/XRDS.php
index 8b24de3..2c386da 100644
--- a/Tests/Services/Yadis/XRDS.php
+++ b/Tests/Services/Yadis/XRDS.php
@@ -6,6 +6,8 @@
require_once 'PHPUnit.php';
require_once 'Services/Yadis/XRDS.php';
+require_once 'Services/Yadis/XRIRes.php';
+require_once 'Services/Yadis/XRI.php';
require_once 'Tests/Services/Yadis/TestUtil.php';
class Tests_Services_Yadis_XRDS extends PHPUnit_TestCase {
@@ -75,6 +77,46 @@ class Tests_Services_Yadis_XRDS extends PHPUnit_TestCase {
$this->assertTrue(Services_Yadis_XRDS::parseXRDS("\x00") === null);
}
+ function test_getCanonicalID()
+ {
+ $canonicalIDtests = array(
+ array("@ootao*test1", "delegated-20060809.xrds",
+ "@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01"),
+ array("@ootao*test1", "delegated-20060809-r1.xrds",
+ "@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01"),
+ array("@ootao*test1", "delegated-20060809-r2.xrds",
+ "@!5BAD.2AA.3C72.AF46!0000.0000.3B9A.CA01"),
+ array("=keturn*isDrummond", "spoof1.xrds", null),
+ array("=keturn*isDrummond", "spoof2.xrds", null),
+ array("@keturn*is*drummond", "spoof3.xrds", null),
+ // Don't let IRI authorities be canonical for the GCS.
+ array("phreak.example.com", "delegated-20060809-r2.xrds", null)
+ // TODO: Refs
+ // ("@ootao*test.ref", "ref.xrds", "@!BAE.A650.823B.2475")
+ );
+
+ foreach ($canonicalIDtests as $tupl) {
+ list($iname, $filename, $expectedID) = $tupl;
+
+ $xml = Tests_Services_Yadis_readdata($filename);
+ $xrds = Services_Yadis_XRDS::parseXRDS($xml);
+ $this->_getCanonicalID($iname, $xrds, $expectedID);
+ }
+ }
+
+ function _getCanonicalID($iname, $xrds, $expectedID)
+ {
+ if ($expectedID === null) {
+ $result =Services_Yadis_getCanonicalID($iname, $xrds);
+ if ($result !== false) {
+ $this->fail($iname.' (got '.$result.')');
+ }
+ } else {
+ $cid = Services_Yadis_getCanonicalID($iname, $xrds);
+ $this->assertEquals($expectedID, $cid);
+ }
+ }
+
function test_services_filters()
{
// First, just be sure that service objects do the right
diff --git a/Tests/Services/Yadis/XRI.php b/Tests/Services/Yadis/XRI.php
index 5d17c93..c37128c 100644
--- a/Tests/Services/Yadis/XRI.php
+++ b/Tests/Services/Yadis/XRI.php
@@ -10,7 +10,9 @@
*/
require_once "PHPUnit.php";
+require_once "Services/Yadis/XRIRes.php";
require_once "Services/Yadis/XRI.php";
+require_once "Services/Yadis/Yadis.php";
class Tests_Services_Yadis_XriDiscoveryTestCase extends PHPUnit_TestCase {
function runTest()
@@ -90,6 +92,37 @@ class Tests_Services_Yadis_ProxyQueryTestCase extends PHPUnit_TestCase {
}
}
+class Tests_Services_Yadis_TestGetRootAuthority extends PHPUnit_TestCase {
+ function runTest()
+ {
+ $xris = array(
+ array("@foo", "@"),
+ array("@foo*bar", "@"),
+ array("@*foo*bar", "@"),
+ array("@foo/bar", "@"),
+ array("!!990!991", "!"),
+ array("!1001!02", "!"),
+ array("=foo*bar", "="),
+ array("(example.com)/foo", "(example.com)"),
+ array("(example.com)*bar/foo", "(example.com)"),
+ array("baz.example.com/foo", "baz.example.com"),
+ array("baz.example.com:8080/foo", "baz.example.com:8080")
+ // Looking at the ABNF in XRI Syntax 2.0, I don't think you can
+ // have example.com*bar. You can do (example.com)*bar, but that
+ // would mean something else.
+ // ("example.com*bar/(=baz)", "example.com*bar"),
+ // ("baz.example.com!01/foo", "baz.example.com!01"),
+ );
+
+ foreach ($xris as $tupl) {
+ list($thexri, $expected_root) = $tupl;
+ $this->assertEquals($expected_root,
+ Services_Yadis_rootAuthority($thexri),
+ 'rootAuthority test ('.$thexri.')');
+ }
+ }
+}
+
class Tests_Services_Yadis_XRI extends PHPUnit_TestSuite {
function getName()
{
@@ -101,6 +134,7 @@ class Tests_Services_Yadis_XRI extends PHPUnit_TestSuite {
$this->addTest(new Tests_Services_Yadis_ProxyQueryTestCase());
$this->addTest(new Tests_Services_Yadis_XriEscapingTestCase());
$this->addTest(new Tests_Services_Yadis_XriDiscoveryTestCase());
+ $this->addTest(new Tests_Services_Yadis_TestGetRootAuthority());
}
}