summaryrefslogtreecommitdiffstats
path: root/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs')
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs410
1 files changed, 410 insertions, 0 deletions
diff --git a/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs b/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs
new file mode 100644
index 0000000..e6e9149
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs
@@ -0,0 +1,410 @@
+using System;
+using System.Linq;
+using System.Net;
+using System.Web;
+using DotNetOpenAuth.OpenId.RelyingParty;
+using DotNetOpenAuth.Test.Mocks;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using DotNetOpenAuth.OpenId;
+
+namespace DotNetOpenAuth.Test.OpenId {
+ [TestClass]
+ public class UriIdentifierTests : OpenIdTestBase {
+ string goodUri = "http://blog.nerdbank.net/";
+ string relativeUri = "host/path";
+ string badUri = "som%-)830w8vf/?.<>,ewackedURI";
+
+ [TestInitialize]
+ public override void SetUp() {
+ if (!UntrustedWebRequest.WhitelistHosts.Contains("localhost"))
+ UntrustedWebRequest.WhitelistHosts.Add("localhost");
+ }
+
+ [TestCleanup]
+ public override void Cleanup() {
+ Mocks.MockHttpRequest.Reset();
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void CtorNullUri() {
+ new UriIdentifier((Uri)null);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void CtorNullString() {
+ new UriIdentifier((string)null);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void CtorBlank() {
+ new UriIdentifier(string.Empty);
+ }
+
+ [TestMethod, ExpectedException(typeof(UriFormatException))]
+ public void CtorBadUri() {
+ new UriIdentifier(badUri);
+ }
+
+ [TestMethod]
+ public void CtorGoodUri() {
+ var uri = new UriIdentifier(goodUri);
+ Assert.AreEqual(new Uri(goodUri), uri.Uri);
+ Assert.IsFalse(uri.SchemeImplicitlyPrepended);
+ Assert.IsFalse(uri.IsDiscoverySecureEndToEnd);
+ }
+
+ [TestMethod]
+ public void CtorStringNoSchemeSecure() {
+ var uri = new UriIdentifier("host/path", true);
+ Assert.AreEqual("https://host/path", uri.Uri.AbsoluteUri);
+ Assert.IsTrue(uri.IsDiscoverySecureEndToEnd);
+ }
+
+ [TestMethod]
+ public void CtorStringHttpsSchemeSecure() {
+ var uri = new UriIdentifier("https://host/path", true);
+ Assert.AreEqual("https://host/path", uri.Uri.AbsoluteUri);
+ Assert.IsTrue(uri.IsDiscoverySecureEndToEnd);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void CtorStringHttpSchemeSecure() {
+ new UriIdentifier("http://host/path", true);
+ }
+
+ [TestMethod]
+ public void CtorUriHttpsSchemeSecure() {
+ var uri = new UriIdentifier(new Uri("https://host/path"), true);
+ Assert.AreEqual("https://host/path", uri.Uri.AbsoluteUri);
+ Assert.IsTrue(uri.IsDiscoverySecureEndToEnd);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void CtorUriHttpSchemeSecure() {
+ new UriIdentifier(new Uri("http://host/path"), true);
+ }
+
+ /// <summary>
+ /// Verifies that the fragment is not stripped from an Identifier.
+ /// </summary>
+ /// <remarks>
+ /// Although fragments should be stripped from user supplied identifiers,
+ /// they should NOT be stripped from claimed identifiers. So the UriIdentifier
+ /// class, which serves both identifier types, must not do the stripping.
+ /// </remarks>
+ [TestMethod]
+ public void DoesNotStripFragment() {
+ Uri original = new Uri("http://a/b#c");
+ UriIdentifier identifier = new UriIdentifier(original);
+ Assert.AreEqual(original.Fragment, identifier.Uri.Fragment);
+ }
+
+ [TestMethod]
+ public void IsValid() {
+ Assert.IsTrue(UriIdentifier.IsValidUri(goodUri));
+ Assert.IsFalse(UriIdentifier.IsValidUri(badUri));
+ Assert.IsTrue(UriIdentifier.IsValidUri(relativeUri), "URL lacking http:// prefix should have worked anyway.");
+ }
+
+ [TestMethod]
+ public void TrimFragment() {
+ Identifier noFragment = UriIdentifier.Parse("http://a/b");
+ Identifier fragment = UriIdentifier.Parse("http://a/b#c");
+ Assert.AreSame(noFragment, noFragment.TrimFragment());
+ Assert.AreEqual(noFragment, fragment.TrimFragment());
+ }
+
+ [TestMethod]
+ public void ToStringTest() {
+ Assert.AreEqual(goodUri, new UriIdentifier(goodUri).ToString());
+ }
+
+ [TestMethod]
+ public void EqualsTest() {
+ Assert.AreEqual(new UriIdentifier(goodUri), new UriIdentifier(goodUri));
+ // This next test is an interesting side-effect of passing off to Uri.Equals. But it's probably ok.
+ Assert.AreEqual(new UriIdentifier(goodUri), new UriIdentifier(goodUri + "#frag"));
+ Assert.AreNotEqual(new UriIdentifier(goodUri), new UriIdentifier(goodUri + "a"));
+ Assert.AreNotEqual(null, new UriIdentifier(goodUri));
+ Assert.AreNotEqual(goodUri, new UriIdentifier(goodUri));
+ }
+
+ [TestMethod]
+ public void UnicodeTest() {
+ string unicodeUrl = "http://nerdbank.org/opaffirmative/崎村.aspx";
+ Assert.IsTrue(UriIdentifier.IsValidUri(unicodeUrl));
+ Identifier id;
+ Assert.IsTrue(UriIdentifier.TryParse(unicodeUrl, out id));
+ Assert.AreEqual("/opaffirmative/%E5%B4%8E%E6%9D%91.aspx", ((UriIdentifier)id).Uri.AbsolutePath);
+ Assert.AreEqual(Uri.EscapeUriString(unicodeUrl), id.ToString());
+ }
+
+ void discover(string url, ProtocolVersion version, Identifier expectedLocalId, bool expectSreg, bool useRedirect) {
+ discover(url, version, expectedLocalId, expectSreg, useRedirect, null);
+ }
+ void discover(string url, ProtocolVersion version, Identifier expectedLocalId, bool expectSreg, bool useRedirect, WebHeaderCollection headers) {
+ Protocol protocol = Protocol.Lookup(version);
+ UriIdentifier claimedId = TestSupport.GetFullUrl(url);
+ UriIdentifier userSuppliedIdentifier = TestSupport.GetFullUrl(
+ "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();
+ }
+ MockHttpRequest.RegisterMockResponse(new Uri(idToDiscover), claimedId, contentType,
+ headers ?? new WebHeaderCollection(), TestSupport.LoadEmbeddedFile(url));
+
+ ServiceEndpoint se = idToDiscover.Discover().FirstOrDefault();
+ Assert.IsNotNull(se, url + " failed to be discovered.");
+ Assert.AreSame(protocol, se.Protocol);
+ Assert.AreEqual(claimedId, se.ClaimedIdentifier);
+ Assert.AreEqual(expectedLocalId, se.ProviderLocalIdentifier);
+ Assert.AreEqual(expectSreg ? 2 : 1, se.ProviderSupportedServiceTypeUris.Length);
+ Assert.IsTrue(Array.IndexOf(se.ProviderSupportedServiceTypeUris, protocol.ClaimedIdentifierServiceTypeURI) >= 0);
+ Assert.AreEqual(expectSreg, se.IsExtensionSupported(new ClaimsRequest()));
+ }
+ void discoverXrds(string page, ProtocolVersion version, Identifier expectedLocalId) {
+ discoverXrds(page, version, expectedLocalId, null);
+ }
+ void discoverXrds(string page, ProtocolVersion version, Identifier expectedLocalId, WebHeaderCollection headers) {
+ if (!page.Contains(".")) page += ".xml";
+ discover("/Discovery/xrdsdiscovery/" + page, version, expectedLocalId, true, false, headers);
+ discover("/Discovery/xrdsdiscovery/" + page, version, expectedLocalId, true, true, headers);
+ }
+ void discoverHtml(string page, ProtocolVersion version, Identifier expectedLocalId, bool useRedirect) {
+ discover("/Discovery/htmldiscovery/" + page, version, expectedLocalId, false, useRedirect);
+ }
+ void discoverHtml(string scenario, ProtocolVersion version, Identifier expectedLocalId) {
+ string page = scenario + ".html";
+ discoverHtml(page, version, expectedLocalId, false);
+ discoverHtml(page, version, expectedLocalId, true);
+ }
+ void failDiscover(string url) {
+ UriIdentifier userSuppliedId = TestSupport.GetFullUrl(url);
+
+ Mocks.MockHttpRequest.RegisterMockResponse(new Uri(userSuppliedId), userSuppliedId, "text/html",
+ TestSupport.LoadEmbeddedFile(url));
+
+ Assert.AreEqual(0, userSuppliedId.Discover().Count()); // ... but that no endpoint info is discoverable
+ }
+ void failDiscoverHtml(string scenario) {
+ failDiscover("/Discovery/htmldiscovery/" + scenario + ".html");
+ }
+ void failDiscoverXrds(string scenario) {
+ failDiscover("/Discovery/xrdsdiscovery/" + scenario + ".xml");
+ }
+ [TestMethod]
+ public void HtmlDiscover_11() {
+ discoverHtml("html10prov", ProtocolVersion.V11, null);
+ discoverHtml("html10both", ProtocolVersion.V11, "http://c/d");
+ failDiscoverHtml("html10del");
+ }
+ [TestMethod]
+ public void HtmlDiscover_20() {
+ discoverHtml("html20prov", ProtocolVersion.V20, null);
+ discoverHtml("html20both", ProtocolVersion.V20, "http://c/d");
+ failDiscoverHtml("html20del");
+ discoverHtml("html2010", ProtocolVersion.V20, "http://c/d");
+ discoverHtml("html1020", ProtocolVersion.V20, "http://c/d");
+ discoverHtml("html2010combinedA", ProtocolVersion.V20, "http://c/d");
+ discoverHtml("html2010combinedB", ProtocolVersion.V20, "http://c/d");
+ discoverHtml("html2010combinedC", ProtocolVersion.V20, "http://c/d");
+ failDiscoverHtml("html20relative");
+ }
+ [TestMethod]
+ public void XrdsDiscoveryFromHead() {
+ Mocks.MockHttpRequest.RegisterMockResponse(new Uri("http://localhost/xrds1020.xml"),
+ "application/xrds+xml", TestSupport.LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds1020.xml"));
+ discoverXrds("XrdsReferencedInHead.html", ProtocolVersion.V10, null);
+ }
+ [TestMethod]
+ public void XrdsDiscoveryFromHttpHeader() {
+ WebHeaderCollection headers = new WebHeaderCollection();
+ headers.Add("X-XRDS-Location", TestSupport.GetFullUrl("http://localhost/xrds1020.xml").AbsoluteUri);
+ Mocks.MockHttpRequest.RegisterMockResponse(new Uri("http://localhost/xrds1020.xml"),
+ "application/xrds+xml", TestSupport.LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds1020.xml"));
+ discoverXrds("XrdsReferencedInHttpHeader.html", ProtocolVersion.V10, null, headers);
+ }
+ [TestMethod]
+ public void XrdsDirectDiscovery_10() {
+ failDiscoverXrds("xrds-irrelevant");
+ discoverXrds("xrds10", ProtocolVersion.V10, null);
+ discoverXrds("xrds11", ProtocolVersion.V11, null);
+ discoverXrds("xrds1020", ProtocolVersion.V10, null);
+ }
+ [TestMethod]
+ public void XrdsDirectDiscovery_20() {
+ discoverXrds("xrds20", ProtocolVersion.V20, null);
+ discoverXrds("xrds2010a", ProtocolVersion.V20, null);
+ discoverXrds("xrds2010b", ProtocolVersion.V20, null);
+ }
+
+ [TestMethod]
+ public void NormalizeCase() {
+ // only the host name can be normalized in casing safely.
+ Identifier id = "http://HOST:80/PaTH?KeY=VaLUE#fRag";
+ Assert.AreEqual("http://host/PaTH?KeY=VaLUE#fRag", id.ToString());
+ // make sure https is preserved, along with port 80, which is NON-default for https
+ id = "https://HOST:80/PaTH?KeY=VaLUE#fRag";
+ Assert.AreEqual("https://host:80/PaTH?KeY=VaLUE#fRag", id.ToString());
+ }
+
+ [TestMethod]
+ public void HttpSchemePrepended() {
+ UriIdentifier id = new UriIdentifier("www.yahoo.com");
+ Assert.AreEqual("http://www.yahoo.com/", id.ToString());
+ Assert.IsTrue(id.SchemeImplicitlyPrepended);
+ }
+
+ ////[TestMethod, Ignore("The spec says http:// must be prepended in this case, but that just creates an invalid URI. Our UntrustedWebRequest will stop disallowed schemes.")]
+ public void CtorDisallowedScheme() {
+ UriIdentifier id = new UriIdentifier(new Uri("ftp://host/path"));
+ Assert.AreEqual("http://ftp://host/path", id.ToString());
+ Assert.IsTrue(id.SchemeImplicitlyPrepended);
+ }
+
+ [TestMethod]
+ public void DiscoveryWithRedirects() {
+ MockHttpRequest.Reset();
+ Identifier claimedId = TestSupport.GetMockIdentifier(TestSupport.Scenarios.AutoApproval, ProtocolVersion.V20);
+
+ // Add a couple of chained redirect pages that lead to the claimedId.
+ Uri userSuppliedUri = TestSupport.GetFullUrl("/someSecurePage", null, true);
+ Uri insecureMidpointUri = TestSupport.GetFullUrl("/insecureStop");
+ MockHttpRequest.RegisterMockRedirect(userSuppliedUri, insecureMidpointUri);
+ MockHttpRequest.RegisterMockRedirect(insecureMidpointUri, new Uri(claimedId.ToString()));
+
+ // don't require secure SSL discovery for this test.
+ Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, false);
+ Assert.AreEqual(1, userSuppliedIdentifier.Discover().Count());
+ }
+
+ [TestMethod]
+ public void TryRequireSslAdjustsIdentifier() {
+ Identifier secureId;
+ // Try Parse and ctor without explicit scheme
+ var id = Identifier.Parse("www.yahoo.com");
+ Assert.AreEqual("http://www.yahoo.com/", id.ToString());
+ Assert.IsTrue(id.TryRequireSsl(out secureId));
+ Assert.IsTrue(secureId.IsDiscoverySecureEndToEnd);
+ Assert.AreEqual("https://www.yahoo.com/", secureId.ToString());
+
+ id = new UriIdentifier("www.yahoo.com");
+ Assert.AreEqual("http://www.yahoo.com/", id.ToString());
+ Assert.IsTrue(id.TryRequireSsl(out secureId));
+ Assert.IsTrue(secureId.IsDiscoverySecureEndToEnd);
+ Assert.AreEqual("https://www.yahoo.com/", secureId.ToString());
+
+ // Try Parse and ctor with explicit http:// scheme
+ id = Identifier.Parse("http://www.yahoo.com");
+ Assert.IsFalse(id.TryRequireSsl(out secureId));
+ Assert.IsFalse(secureId.IsDiscoverySecureEndToEnd);
+ Assert.AreEqual("http://www.yahoo.com/", secureId.ToString());
+ Assert.AreEqual(0, secureId.Discover().Count());
+
+ id = new UriIdentifier("http://www.yahoo.com");
+ Assert.IsFalse(id.TryRequireSsl(out secureId));
+ Assert.IsFalse(secureId.IsDiscoverySecureEndToEnd);
+ Assert.AreEqual("http://www.yahoo.com/", secureId.ToString());
+ Assert.AreEqual(0, secureId.Discover().Count());
+ }
+
+ [TestMethod]
+ public void DiscoverRequireSslWithSecureRedirects() {
+ MockHttpRequest.Reset();
+ Identifier claimedId = TestSupport.GetMockIdentifier(TestSupport.Scenarios.AutoApproval, ProtocolVersion.V20, true);
+
+ // Add a couple of chained redirect pages that lead to the claimedId.
+ // All redirects should be secure.
+ Uri userSuppliedUri = TestSupport.GetFullUrl("/someSecurePage", null, true);
+ Uri secureMidpointUri = TestSupport.GetFullUrl("/secureStop", null, true);
+ MockHttpRequest.RegisterMockRedirect(userSuppliedUri, secureMidpointUri);
+ MockHttpRequest.RegisterMockRedirect(secureMidpointUri, new Uri(claimedId.ToString()));
+
+ Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, true);
+ Assert.AreEqual(1, userSuppliedIdentifier.Discover().Count());
+ }
+
+ [TestMethod, ExpectedException(typeof(OpenIdException))]
+ public void DiscoverRequireSslWithInsecureRedirect() {
+ MockHttpRequest.Reset();
+ Identifier claimedId = TestSupport.GetMockIdentifier(TestSupport.Scenarios.AutoApproval, 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 = TestSupport.GetFullUrl("/someSecurePage", null, true);
+ Uri insecureMidpointUri = TestSupport.GetFullUrl("/insecureStop");
+ MockHttpRequest.RegisterMockRedirect(userSuppliedUri, insecureMidpointUri);
+ MockHttpRequest.RegisterMockRedirect(insecureMidpointUri, new Uri(claimedId.ToString()));
+
+ Identifier userSuppliedIdentifier = new UriIdentifier(userSuppliedUri, true);
+ userSuppliedIdentifier.Discover();
+ }
+
+ [TestMethod]
+ public void DiscoveryRequireSslWithInsecureXrdsInSecureHtmlHead() {
+ var insecureXrdsSource = TestSupport.GetMockIdentifier(TestSupport.Scenarios.AutoApproval, ProtocolVersion.V20, false);
+ Uri secureClaimedUri = TestSupport.GetFullUrl("/secureId", null, true);
+
+ string html = string.Format("<html><head><meta http-equiv='X-XRDS-Location' content='{0}'/></head><body></body></html>",
+ insecureXrdsSource);
+ MockHttpRequest.RegisterMockResponse(secureClaimedUri, "text/html", html);
+
+ Identifier userSuppliedIdentifier = new UriIdentifier(secureClaimedUri, true);
+ Assert.AreEqual(0, userSuppliedIdentifier.Discover().Count());
+ }
+
+ [TestMethod]
+ public void DiscoveryRequireSslWithInsecureXrdsInSecureHttpHeader() {
+ var insecureXrdsSource = TestSupport.GetMockIdentifier(TestSupport.Scenarios.AutoApproval, ProtocolVersion.V20, false);
+ Uri secureClaimedUri = TestSupport.GetFullUrl("/secureId", null, true);
+
+ string html = "<html><head></head><body></body></html>";
+ WebHeaderCollection headers = new WebHeaderCollection {
+ { "X-XRDS-Location", insecureXrdsSource }
+ };
+ MockHttpRequest.RegisterMockResponse(secureClaimedUri, secureClaimedUri, "text/html", headers, html);
+
+ Identifier userSuppliedIdentifier = new UriIdentifier(secureClaimedUri, true);
+ Assert.AreEqual(0, userSuppliedIdentifier.Discover().Count());
+ }
+
+ [TestMethod]
+ public void DiscoveryRequireSslWithInsecureXrdsButSecureLinkTags() {
+ var insecureXrdsSource = TestSupport.GetMockIdentifier(TestSupport.Scenarios.AutoApproval, ProtocolVersion.V20, false);
+ Uri secureClaimedUri = TestSupport.GetFullUrl("/secureId", null, true);
+
+ Identifier localIdForLinkTag = TestSupport.GetDelegateUrl(TestSupport.Scenarios.AlwaysDeny, true);
+ 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(TestSupport.GetFullUrl("/" + TestSupport.ProviderPage, null, true).AbsoluteUri),
+ HttpUtility.HtmlEncode(localIdForLinkTag.ToString()));
+ MockHttpRequest.RegisterMockResponse(secureClaimedUri, "text/html", html);
+
+ Identifier userSuppliedIdentifier = new UriIdentifier(secureClaimedUri, true);
+ Assert.AreEqual(localIdForLinkTag, userSuppliedIdentifier.Discover().Single().ProviderLocalIdentifier);
+ }
+
+ [TestMethod]
+ public void DiscoveryRequiresSslIgnoresInsecureEndpointsInXrds() {
+ var insecureEndpoint = TestSupport.GetServiceEndpoint(TestSupport.Scenarios.AutoApproval, ProtocolVersion.V20, 10, false);
+ var secureEndpoint = TestSupport.GetServiceEndpoint(TestSupport.Scenarios.ApproveOnSetup, ProtocolVersion.V20, 20, true);
+ UriIdentifier secureClaimedId = new UriIdentifier(TestSupport.GetFullUrl("/claimedId", null, true), true);
+ MockHttpRequest.RegisterMockXrdsResponse(secureClaimedId, new ServiceEndpoint[] { insecureEndpoint, secureEndpoint });
+ Assert.AreEqual(secureEndpoint.ProviderLocalIdentifier, secureClaimedId.Discover().Single().ProviderLocalIdentifier);
+ }
+ }
+}