diff options
Diffstat (limited to 'src/DotNetOpenAuth.Test/OpenId/DiscoveryServices/UriDiscoveryServiceTests.cs')
-rw-r--r-- | src/DotNetOpenAuth.Test/OpenId/DiscoveryServices/UriDiscoveryServiceTests.cs | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/src/DotNetOpenAuth.Test/OpenId/DiscoveryServices/UriDiscoveryServiceTests.cs b/src/DotNetOpenAuth.Test/OpenId/DiscoveryServices/UriDiscoveryServiceTests.cs new file mode 100644 index 0000000..1050b4b --- /dev/null +++ b/src/DotNetOpenAuth.Test/OpenId/DiscoveryServices/UriDiscoveryServiceTests.cs @@ -0,0 +1,303 @@ +//----------------------------------------------------------------------- +// <copyright file="UriDiscoveryServiceTests.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Test.OpenId.DiscoveryServices { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net; + using System.Text; + using System.Web; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OpenId; + using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; + using DotNetOpenAuth.OpenId.RelyingParty; + using NUnit.Framework; + + [TestFixture] + public class UriDiscoveryServiceTests : OpenIdTestBase { + [TestCase] + public void DiscoveryWithRedirects() { + Identifier claimedId = this.GetMockIdentifier(ProtocolVersion.V20, false); + + // Add a couple of chained redirect pages that lead to the claimedId. + Uri userSuppliedUri = new Uri("https://localhost/someSecurePage"); + Uri insecureMidpointUri = new Uri("http://localhost/insecureStop"); + this.MockResponder.RegisterMockRedirect(userSuppliedUri, insecureMidpointUri); + this.MockResponder.RegisterMockRedirect(insecureMidpointUri, new Uri(claimedId.ToString())); + + // don't require secure SSL discovery for this test. + Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, false); + Assert.AreEqual(1, this.Discover(userSuppliedIdentifier).Count()); + } + + [TestCase] + public void DiscoverRequireSslWithSecureRedirects() { + Identifier claimedId = this.GetMockIdentifier(ProtocolVersion.V20, true); + + // Add a couple of chained redirect pages that lead to the claimedId. + // All redirects should be secure. + Uri userSuppliedUri = new Uri("https://localhost/someSecurePage"); + Uri secureMidpointUri = new Uri("https://localhost/secureStop"); + this.MockResponder.RegisterMockRedirect(userSuppliedUri, secureMidpointUri); + this.MockResponder.RegisterMockRedirect(secureMidpointUri, new Uri(claimedId.ToString())); + + Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, true); + Assert.AreEqual(1, this.Discover(userSuppliedIdentifier).Count()); + } + + [TestCase, ExpectedException(typeof(ProtocolException))] + public void DiscoverRequireSslWithInsecureRedirect() { + Identifier claimedId = this.GetMockIdentifier(ProtocolVersion.V20, true); + + // Add a couple of chained redirect pages that lead to the claimedId. + // Include an insecure HTTP jump in those redirects to verify that + // the ultimate endpoint is never found as a result of high security profile. + Uri userSuppliedUri = new Uri("https://localhost/someSecurePage"); + Uri insecureMidpointUri = new Uri("http://localhost/insecureStop"); + this.MockResponder.RegisterMockRedirect(userSuppliedUri, insecureMidpointUri); + this.MockResponder.RegisterMockRedirect(insecureMidpointUri, new Uri(claimedId.ToString())); + + Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, true); + this.Discover(userSuppliedIdentifier); + } + + [TestCase] + public void DiscoveryRequireSslWithInsecureXrdsInSecureHtmlHead() { + var insecureXrdsSource = this.GetMockIdentifier(ProtocolVersion.V20, false); + Uri secureClaimedUri = new Uri("https://localhost/secureId"); + + string html = string.Format("<html><head><meta http-equiv='X-XRDS-Location' content='{0}'/></head><body></body></html>", insecureXrdsSource); + this.MockResponder.RegisterMockResponse(secureClaimedUri, "text/html", html); + + Identifier userSuppliedIdentifier = new UriIdentifier(secureClaimedUri, true); + Assert.AreEqual(0, this.Discover(userSuppliedIdentifier).Count()); + } + + [TestCase] + public void DiscoveryRequireSslWithInsecureXrdsInSecureHttpHeader() { + var insecureXrdsSource = this.GetMockIdentifier(ProtocolVersion.V20, false); + + string html = "<html><head></head><body></body></html>"; + WebHeaderCollection headers = new WebHeaderCollection { + { "X-XRDS-Location", insecureXrdsSource } + }; + this.MockResponder.RegisterMockResponse(VanityUriSsl, VanityUriSsl, "text/html", headers, html); + + Identifier userSuppliedIdentifier = new UriIdentifier(VanityUriSsl, true); + Assert.AreEqual(0, this.Discover(userSuppliedIdentifier).Count()); + } + + [TestCase] + public void DiscoveryRequireSslWithInsecureXrdsButSecureLinkTags() { + var insecureXrdsSource = this.GetMockIdentifier(ProtocolVersion.V20, false); + string html = string.Format( + @" + <html><head> + <meta http-equiv='X-XRDS-Location' content='{0}'/> <!-- this one will be insecure and ignored --> + <link rel='openid2.provider' href='{1}' /> + <link rel='openid2.local_id' href='{2}' /> + </head><body></body></html>", + HttpUtility.HtmlEncode(insecureXrdsSource), + HttpUtility.HtmlEncode(OPUriSsl.AbsoluteUri), + HttpUtility.HtmlEncode(OPLocalIdentifiersSsl[1].AbsoluteUri)); + this.MockResponder.RegisterMockResponse(VanityUriSsl, "text/html", html); + + Identifier userSuppliedIdentifier = new UriIdentifier(VanityUriSsl, true); + + // We verify that the XRDS was ignored and the LINK tags were used + // because the XRDS OP-LocalIdentifier uses different local identifiers. + Assert.AreEqual(OPLocalIdentifiersSsl[1].AbsoluteUri, this.Discover(userSuppliedIdentifier).Single().ProviderLocalIdentifier.ToString()); + } + + [TestCase] + public void DiscoveryRequiresSslIgnoresInsecureEndpointsInXrds() { + var insecureEndpoint = GetServiceEndpoint(0, ProtocolVersion.V20, 10, false); + var secureEndpoint = GetServiceEndpoint(1, ProtocolVersion.V20, 20, true); + UriIdentifier secureClaimedId = new UriIdentifier(VanityUriSsl, true); + this.MockResponder.RegisterMockXrdsResponse(secureClaimedId, new IdentifierDiscoveryResult[] { insecureEndpoint, secureEndpoint }); + Assert.AreEqual(secureEndpoint.ProviderLocalIdentifier, this.Discover(secureClaimedId).Single().ProviderLocalIdentifier); + } + + [TestCase] + public void XrdsDirectDiscovery_10() { + this.FailDiscoverXrds("xrds-irrelevant"); + 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"); + } + + [TestCase] + public void XrdsDirectDiscovery_20() { + 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"); + } + + [TestCase] + public void HtmlDiscover_11() { + 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"); + } + + [TestCase] + public void HtmlDiscover_20() { + 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", "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"); + } + + [TestCase] + 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, "http://a/b"); + } + + [TestCase] + public void XrdsDiscoveryFromHttpHeader() { + 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, "http://a/b", headers); + } + + /// <summary> + /// Verifies HTML discovery proceeds if an XRDS document is referenced that doesn't contain OpenID endpoints. + /// </summary> + [TestCase] + public void HtmlDiscoveryProceedsIfXrdsIsEmpty() { + this.MockResponder.RegisterMockResponse(new Uri("http://localhost/xrds-irrelevant.xml"), "application/xrds+xml", LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds-irrelevant.xml")); + this.DiscoverHtml("html20provWithEmptyXrds", ProtocolVersion.V20, null, "http://a/b"); + } + + /// <summary> + /// Verifies HTML discovery proceeds if the XRDS that is referenced cannot be found. + /// </summary> + [TestCase] + public void HtmlDiscoveryProceedsIfXrdsIsBadOrMissing() { + this.DiscoverHtml("html20provWithBadXrds", ProtocolVersion.V20, null, "http://a/b"); + } + + /// <summary> + /// Verifies that a dual identifier yields only one service endpoint by default. + /// </summary> + [TestCase] + public void DualIdentifierOffByDefault() { + this.MockResponder.RegisterMockResponse(VanityUri, "application/xrds+xml", LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds20dual.xml")); + var results = this.Discover(VanityUri).ToList(); + Assert.AreEqual(1, results.Count(r => r.ClaimedIdentifier == r.Protocol.ClaimedIdentifierForOPIdentifier), "OP Identifier missing from discovery results."); + Assert.AreEqual(1, results.Count, "Unexpected additional services discovered."); + } + + /// <summary> + /// Verifies that a dual identifier yields two service endpoints when that feature is turned on. + /// </summary> + [TestCase] + public void DualIdentifier() { + this.MockResponder.RegisterMockResponse(VanityUri, "application/xrds+xml", LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds20dual.xml")); + var rp = this.CreateRelyingParty(true); + rp.Channel.WebRequestHandler = this.RequestHandler; + rp.SecuritySettings.AllowDualPurposeIdentifiers = true; + var results = rp.Discover(VanityUri).ToList(); + Assert.AreEqual(1, results.Count(r => r.ClaimedIdentifier == r.Protocol.ClaimedIdentifierForOPIdentifier), "OP Identifier missing from discovery results."); + Assert.AreEqual(1, results.Count(r => r.ClaimedIdentifier == VanityUri), "Claimed identifier missing from discovery results."); + Assert.AreEqual(2, results.Count, "Unexpected additional services discovered."); + } + + 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, 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); + UriIdentifier userSuppliedIdentifier = new Uri(baseUrl, "Discovery/htmldiscovery/redirect.aspx?target=" + url); + if (expectedLocalId == null) { + expectedLocalId = claimedId; + } + Identifier idToDiscover = useRedirect ? userSuppliedIdentifier : claimedId; + + string contentType; + if (url.EndsWith("html")) { + contentType = "text/html"; + } else if (url.EndsWith("xml")) { + contentType = "application/xrds+xml"; + } else { + throw new InvalidOperationException(); + } + this.MockResponder.RegisterMockResponse(new Uri(idToDiscover), claimedId, contentType, headers ?? new WebHeaderCollection(), LoadEmbeddedFile(url)); + + IdentifierDiscoveryResult expected = IdentifierDiscoveryResult.CreateForClaimedIdentifier( + claimedId, + expectedLocalId, + new ProviderEndpointDescription(new Uri(providerEndpoint), new string[] { protocol.ClaimedIdentifierServiceTypeURI }), // services aren't checked by Equals + null, + null); + + IdentifierDiscoveryResult se = this.Discover(idToDiscover).FirstOrDefault(ep => ep.Equals(expected)); + Assert.IsNotNull(se, url + " failed to be discovered."); + + // Do extra checking of service type URIs, which aren't included in + // the ServiceEndpoint.Equals method. + Assert.AreEqual(expectSreg ? 2 : 1, se.Capabilities.Count); + Assert.IsTrue(se.Capabilities.Contains(protocol.ClaimedIdentifierServiceTypeURI)); + Assert.AreEqual(expectSreg, se.IsExtensionSupported<ClaimsRequest>()); + } + + 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, string providerEndpoint, WebHeaderCollection headers) { + if (!page.Contains(".")) { + page += ".xml"; + } + 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, string providerEndpoint, bool useRedirect) { + this.Discover("/Discovery/htmldiscovery/" + page, version, expectedLocalId, providerEndpoint, false, useRedirect); + } + + private void DiscoverHtml(string scenario, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint) { + string page = scenario + ".html"; + this.DiscoverHtml(page, version, expectedLocalId, providerEndpoint, false); + this.DiscoverHtml(page, version, expectedLocalId, providerEndpoint, true); + } + + private void FailDiscover(string url) { + UriIdentifier userSuppliedId = new Uri(new Uri("http://localhost"), url); + + this.MockResponder.RegisterMockResponse(new Uri(userSuppliedId), userSuppliedId, "text/html", LoadEmbeddedFile(url)); + + Assert.AreEqual(0, this.Discover(userSuppliedId).Count()); // ... but that no endpoint info is discoverable + } + + private void FailDiscoverHtml(string scenario) { + this.FailDiscover("/Discovery/htmldiscovery/" + scenario + ".html"); + } + + private void FailDiscoverXrds(string scenario) { + this.FailDiscover("/Discovery/xrdsdiscovery/" + scenario + ".xml"); + } + } +} |