diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2009-02-14 14:16:54 -0800 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2009-02-14 14:31:00 -0800 |
commit | 3fc46ddde2dfca5368c59e5df1933f8c08f4fbff (patch) | |
tree | fc39b249678dbd2f077496490b8460d02b58d3e6 | |
parent | 404e7ff2c44ee18b1412238a1154f34891140325 (diff) | |
download | DotNetOpenAuth-3fc46ddde2dfca5368c59e5df1933f8c08f4fbff.zip DotNetOpenAuth-3fc46ddde2dfca5368c59e5df1933f8c08f4fbff.tar.gz DotNetOpenAuth-3fc46ddde2dfca5368c59e5df1933f8c08f4fbff.tar.bz2 |
HTML discovery can now generate multiple endpoints.
This provides a workaround to Google Code Issue 180.
See also 7d339d343f83ce69e6ed876911c834927b8ca88f, which fixes this in the v2.5 branch.
-rw-r--r-- | src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs | 84 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OpenId/UriIdentifier.cs | 85 |
2 files changed, 86 insertions, 83 deletions
diff --git a/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs b/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs index f3ed95e..0aa43d4 100644 --- a/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs @@ -142,28 +142,35 @@ namespace DotNetOpenAuth.Test.OpenId { [TestMethod] public void HtmlDiscover_11() { - this.DiscoverHtml("html10prov", ProtocolVersion.V11, null); - this.DiscoverHtml("html10both", ProtocolVersion.V11, "http://c/d"); + this.DiscoverHtml("html10prov", ProtocolVersion.V11, null, "http://a/b"); + this.DiscoverHtml("html10both", ProtocolVersion.V11, "http://c/d", "http://a/b"); this.FailDiscoverHtml("html10del"); + + // Verify that HTML discovery generates the 1.x endpoints when appropriate + this.DiscoverHtml("html2010", ProtocolVersion.V11, "http://g/h", "http://e/f"); + this.DiscoverHtml("html1020", ProtocolVersion.V11, "http://g/h", "http://e/f"); + this.DiscoverHtml("html2010combinedA", ProtocolVersion.V11, "http://c/d", "http://a/b"); + this.DiscoverHtml("html2010combinedB", ProtocolVersion.V11, "http://c/d", "http://a/b"); + this.DiscoverHtml("html2010combinedC", ProtocolVersion.V11, "http://c/d", "http://a/b"); } [TestMethod] public void HtmlDiscover_20() { - this.DiscoverHtml("html20prov", ProtocolVersion.V20, null); - this.DiscoverHtml("html20both", ProtocolVersion.V20, "http://c/d"); + this.DiscoverHtml("html20prov", ProtocolVersion.V20, null, "http://a/b"); + this.DiscoverHtml("html20both", ProtocolVersion.V20, "http://c/d", "http://a/b"); this.FailDiscoverHtml("html20del"); - this.DiscoverHtml("html2010", ProtocolVersion.V20, "http://c/d"); - this.DiscoverHtml("html1020", ProtocolVersion.V20, "http://c/d"); - this.DiscoverHtml("html2010combinedA", ProtocolVersion.V20, "http://c/d"); - this.DiscoverHtml("html2010combinedB", ProtocolVersion.V20, "http://c/d"); - this.DiscoverHtml("html2010combinedC", ProtocolVersion.V20, "http://c/d"); + this.DiscoverHtml("html2010", ProtocolVersion.V20, "http://c/d", "http://a/b"); + this.DiscoverHtml("html1020", ProtocolVersion.V20, "http://c/d", "http://a/b"); + this.DiscoverHtml("html2010combinedA", ProtocolVersion.V20, "http://c/d", "http://a/b"); + this.DiscoverHtml("html2010combinedB", ProtocolVersion.V20, "http://c/d", "http://a/b"); + this.DiscoverHtml("html2010combinedC", ProtocolVersion.V20, "http://c/d", "http://a/b"); this.FailDiscoverHtml("html20relative"); } [TestMethod] public void XrdsDiscoveryFromHead() { this.MockResponder.RegisterMockResponse(new Uri("http://localhost/xrds1020.xml"), "application/xrds+xml", LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds1020.xml")); - this.DiscoverXrds("XrdsReferencedInHead.html", ProtocolVersion.V10, null); + this.DiscoverXrds("XrdsReferencedInHead.html", ProtocolVersion.V10, null, "http://a/b"); } [TestMethod] @@ -171,22 +178,22 @@ namespace DotNetOpenAuth.Test.OpenId { WebHeaderCollection headers = new WebHeaderCollection(); headers.Add("X-XRDS-Location", new Uri("http://localhost/xrds1020.xml").AbsoluteUri); this.MockResponder.RegisterMockResponse(new Uri("http://localhost/xrds1020.xml"), "application/xrds+xml", LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds1020.xml")); - this.DiscoverXrds("XrdsReferencedInHttpHeader.html", ProtocolVersion.V10, null, headers); + this.DiscoverXrds("XrdsReferencedInHttpHeader.html", ProtocolVersion.V10, null, "http://a/b", headers); } [TestMethod] public void XrdsDirectDiscovery_10() { this.FailDiscoverXrds("xrds-irrelevant"); - this.DiscoverXrds("xrds10", ProtocolVersion.V10, null); - this.DiscoverXrds("xrds11", ProtocolVersion.V11, null); - this.DiscoverXrds("xrds1020", ProtocolVersion.V10, null); + this.DiscoverXrds("xrds10", ProtocolVersion.V10, null, "http://a/b"); + this.DiscoverXrds("xrds11", ProtocolVersion.V11, null, "http://a/b"); + this.DiscoverXrds("xrds1020", ProtocolVersion.V10, null, "http://a/b"); } [TestMethod] public void XrdsDirectDiscovery_20() { - this.DiscoverXrds("xrds20", ProtocolVersion.V20, null); - this.DiscoverXrds("xrds2010a", ProtocolVersion.V20, null); - this.DiscoverXrds("xrds2010b", ProtocolVersion.V20, null); + this.DiscoverXrds("xrds20", ProtocolVersion.V20, null, "http://a/b"); + this.DiscoverXrds("xrds2010a", ProtocolVersion.V20, null, "http://a/b"); + this.DiscoverXrds("xrds2010b", ProtocolVersion.V20, null, "http://a/b"); } [TestMethod] @@ -346,11 +353,11 @@ namespace DotNetOpenAuth.Test.OpenId { Assert.AreEqual(secureEndpoint.ProviderLocalIdentifier, secureClaimedId.Discover(this.RequestHandler).Single().ProviderLocalIdentifier); } - private void Discover(string url, ProtocolVersion version, Identifier expectedLocalId, bool expectSreg, bool useRedirect) { - this.Discover(url, version, expectedLocalId, expectSreg, useRedirect, null); + private void Discover(string url, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, bool expectSreg, bool useRedirect) { + this.Discover(url, version, expectedLocalId, providerEndpoint, expectSreg, useRedirect, null); } - private void Discover(string url, ProtocolVersion version, Identifier expectedLocalId, bool expectSreg, bool useRedirect, WebHeaderCollection headers) { + private void Discover(string url, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, bool expectSreg, bool useRedirect, WebHeaderCollection headers) { Protocol protocol = Protocol.Lookup(version); Uri baseUrl = new Uri("http://localhost/"); UriIdentifier claimedId = new Uri(baseUrl, url); @@ -370,36 +377,45 @@ namespace DotNetOpenAuth.Test.OpenId { } this.MockResponder.RegisterMockResponse(new Uri(idToDiscover), claimedId, contentType, headers ?? new WebHeaderCollection(), LoadEmbeddedFile(url)); - ServiceEndpoint se = idToDiscover.Discover(this.RequestHandler).FirstOrDefault(); + ServiceEndpoint expected = ServiceEndpoint.CreateForClaimedIdentifier( + claimedId, + expectedLocalId, + new ProviderEndpointDescription( + new Uri(providerEndpoint), + new string[] { protocol.ClaimedIdentifierServiceTypeURI }), // this isn't checked by Equals + null, + null); + + ServiceEndpoint se = idToDiscover.Discover(this.RequestHandler).FirstOrDefault(ep => ep.Equals(expected)); Assert.IsNotNull(se, url + " failed to be discovered."); - Assert.AreSame(protocol, se.Protocol); - Assert.AreEqual(claimedId, se.ClaimedIdentifier); - Assert.AreEqual(expectedLocalId, se.ProviderLocalIdentifier); + + // Do extra checking of service type URIs, which aren't included in + // the ServiceEndpoint.Equals method. Assert.AreEqual(expectSreg ? 2 : 1, se.ProviderSupportedServiceTypeUris.Count); Assert.IsTrue(se.ProviderSupportedServiceTypeUris.Contains(protocol.ClaimedIdentifierServiceTypeURI)); Assert.AreEqual(expectSreg, se.IsExtensionSupported(new ClaimsRequest())); } - private void DiscoverXrds(string page, ProtocolVersion version, Identifier expectedLocalId) { - this.DiscoverXrds(page, version, expectedLocalId, null); + private void DiscoverXrds(string page, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint) { + this.DiscoverXrds(page, version, expectedLocalId, providerEndpoint, null); } - private void DiscoverXrds(string page, ProtocolVersion version, Identifier expectedLocalId, WebHeaderCollection headers) { + private void DiscoverXrds(string page, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, WebHeaderCollection headers) { if (!page.Contains(".")) { page += ".xml"; } - this.Discover("/Discovery/xrdsdiscovery/" + page, version, expectedLocalId, true, false, headers); - this.Discover("/Discovery/xrdsdiscovery/" + page, version, expectedLocalId, true, true, headers); + this.Discover("/Discovery/xrdsdiscovery/" + page, version, expectedLocalId, providerEndpoint, true, false, headers); + this.Discover("/Discovery/xrdsdiscovery/" + page, version, expectedLocalId, providerEndpoint, true, true, headers); } - private void DiscoverHtml(string page, ProtocolVersion version, Identifier expectedLocalId, bool useRedirect) { - this.Discover("/Discovery/htmldiscovery/" + page, version, expectedLocalId, false, useRedirect); + private void DiscoverHtml(string page, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint, bool useRedirect) { + this.Discover("/Discovery/htmldiscovery/" + page, version, expectedLocalId, providerEndpoint, false, useRedirect); } - private void DiscoverHtml(string scenario, ProtocolVersion version, Identifier expectedLocalId) { + private void DiscoverHtml(string scenario, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint) { string page = scenario + ".html"; - this.DiscoverHtml(page, version, expectedLocalId, false); - this.DiscoverHtml(page, version, expectedLocalId, true); + this.DiscoverHtml(page, version, expectedLocalId, providerEndpoint, false); + this.DiscoverHtml(page, version, expectedLocalId, providerEndpoint, true); } private void FailDiscover(string url) { diff --git a/src/DotNetOpenAuth/OpenId/UriIdentifier.cs b/src/DotNetOpenAuth/OpenId/UriIdentifier.cs index a55f1d8..4718d8a 100644 --- a/src/DotNetOpenAuth/OpenId/UriIdentifier.cs +++ b/src/DotNetOpenAuth/OpenId/UriIdentifier.cs @@ -222,14 +222,13 @@ namespace DotNetOpenAuth.OpenId { // Failing YADIS discovery of an XRDS document, we try HTML discovery. if (endpoints.Count == 0) { - ServiceEndpoint ep = DiscoverFromHtml(yadisResult.NormalizedUri, this, yadisResult.ResponseText); - if (ep != null) { - Logger.Debug("HTML discovery found a service endpoint."); - Logger.Debug(ep); - if (!IsDiscoverySecureEndToEnd || ep.IsSecure) { - endpoints.Add(ep); - } else { - Logger.Info("Skipping HTML discovered endpoint because it is not secure."); + var htmlEndpoints = new List<ServiceEndpoint>(DiscoverFromHtml(yadisResult.NormalizedUri, this, yadisResult.ResponseText)); + if (htmlEndpoints.Any()) { + Logger.DebugFormat("Total services discovered in HTML: {0}", htmlEndpoints.Count); + Logger.Debug(htmlEndpoints.ToStringDeferred(true)); + endpoints.AddRange(htmlEndpoints.Where(ep => !IsDiscoverySecureEndToEnd || ep.IsSecure)); + if (endpoints.Count == 0) { + Logger.Info("No HTML discovered endpoints met the security requirements."); } } else { Logger.Debug("HTML discovery failed to find any endpoints."); @@ -313,54 +312,42 @@ namespace DotNetOpenAuth.OpenId { /// <param name="userSuppliedIdentifier">The user supplied identifier.</param> /// <param name="html">The HTML that was downloaded and should be searched.</param> /// <returns> - /// An initialized ServiceEndpoint if the OpenID Provider information was - /// found. Otherwise null. + /// A sequence of any discovered ServiceEndpoints. /// </returns> - /// <remarks> - /// OpenID 2.0 tags are always used if they are present, otherwise - /// OpenID 1.x tags are used if present. - /// </remarks> - private static ServiceEndpoint DiscoverFromHtml(Uri claimedIdentifier, UriIdentifier userSuppliedIdentifier, string html) { - Uri providerEndpoint = null; - Protocol discoveredProtocol = null; - Identifier providerLocalIdentifier = null; + private static IEnumerable<ServiceEndpoint> DiscoverFromHtml(Uri claimedIdentifier, UriIdentifier userSuppliedIdentifier, string html) { var linkTags = new List<HtmlLink>(HtmlParser.HeadTags<HtmlLink>(html)); - foreach (var protocol in Protocol.AllVersions) { - foreach (var linkTag in linkTags) { - // rel attributes are supposed to be interpreted with case INsensitivity, - // and is a space-delimited list of values. (http://www.htmlhelp.com/reference/html40/values.html#linktypes) - if (Regex.IsMatch(linkTag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryProviderKey) + @"\b", RegexOptions.IgnoreCase)) { - if (Uri.TryCreate(linkTag.Href, UriKind.Absolute, out providerEndpoint)) { - discoveredProtocol = protocol; - break; - } - } - } - if (providerEndpoint != null) { - break; + foreach (var protocol in Protocol.AllPracticalVersions) { + // rel attributes are supposed to be interpreted with case INsensitivity, + // and is a space-delimited list of values. (http://www.htmlhelp.com/reference/html40/values.html#linktypes) + var serverLinkTag = linkTags.FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryProviderKey) + @"\b", RegexOptions.IgnoreCase)); + if (serverLinkTag == null) { + continue; } - } - if (providerEndpoint == null) { - return null; // html did not contain openid.server link - } - // See if a LocalId tag of the discovered version exists - foreach (var linkTag in linkTags) { - if (Regex.IsMatch(linkTag.Attributes["rel"], @"\b" + Regex.Escape(discoveredProtocol.HtmlDiscoveryLocalIdKey) + @"\b", RegexOptions.IgnoreCase)) { - if (Identifier.IsValid(linkTag.Href)) { - providerLocalIdentifier = linkTag.Href; - break; - } else { - Logger.WarnFormat("Skipping endpoint data because local id is badly formed ({0}).", linkTag.Href); - return null; // badly formed URL used as LocalId + Uri providerEndpoint = null; + if (Uri.TryCreate(serverLinkTag.Href, UriKind.Absolute, out providerEndpoint)) { + // See if a LocalId tag of the discovered version exists + Identifier providerLocalIdentifier = null; + var delegateLinkTag = linkTags.FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryLocalIdKey) + @"\b", RegexOptions.IgnoreCase)); + if (delegateLinkTag != null) { + if (Identifier.IsValid(delegateLinkTag.Href)) { + providerLocalIdentifier = delegateLinkTag.Href; + } else { + Logger.WarnFormat("Skipping endpoint data because local id is badly formed ({0}).", delegateLinkTag.Href); + continue; // skip to next version + } } + + // Choose the TypeURI to match the OpenID version detected. + string[] typeURIs = { protocol.ClaimedIdentifierServiceTypeURI }; + yield return ServiceEndpoint.CreateForClaimedIdentifier( + claimedIdentifier, + providerLocalIdentifier, + new ProviderEndpointDescription(providerEndpoint, typeURIs), + (int?)null, + (int?)null); } } - - // Choose the TypeURI to match the OpenID version detected. - string[] typeURIs = { discoveredProtocol.ClaimedIdentifierServiceTypeURI }; - var providerDescription = new ProviderEndpointDescription(providerEndpoint, typeURIs); - return ServiceEndpoint.CreateForClaimedIdentifier(claimedIdentifier, userSuppliedIdentifier, providerLocalIdentifier, providerDescription, (int?)null, (int?)null); } /// <summary> |