1
//-----------------------------------------------------------------------
2 // <copyright file="UriIdentifierTests.cs" company="Andrew Arnott">
3 // Copyright (c) Andrew Arnott. All rights reserved.
5 //-----------------------------------------------------------------------
7 namespace DotNetOpenAuth
.Test
.OpenId
{
12 using DotNetOpenAuth
.Messaging
;
13 using DotNetOpenAuth
.OpenId
;
14 using DotNetOpenAuth
.OpenId
.RelyingParty
;
15 using Microsoft
.VisualStudio
.TestTools
.UnitTesting
;
18 public class UriIdentifierTests
: OpenIdTestBase
{
19 private string goodUri
= "http://blog.nerdbank.net/";
20 private string relativeUri
= "host/path";
21 private string badUri
= "som%-)830w8vf/?.<>,ewackedURI";
24 public override void SetUp() {
28 [TestMethod
, ExpectedException(typeof(ArgumentNullException
))]
29 public void CtorNullUri() {
30 new UriIdentifier((Uri
)null);
33 [TestMethod
, ExpectedException(typeof(ArgumentNullException
))]
34 public void CtorNullString() {
35 new UriIdentifier((string)null);
38 [TestMethod
, ExpectedException(typeof(ArgumentException
))]
39 public void CtorBlank() {
40 new UriIdentifier(string.Empty
);
43 [TestMethod
, ExpectedException(typeof(UriFormatException
))]
44 public void CtorBadUri() {
45 new UriIdentifier(this.badUri
);
49 public void CtorGoodUri() {
50 var uri
= new UriIdentifier(this.goodUri
);
51 Assert
.AreEqual(new Uri(this.goodUri
), uri
.Uri
);
52 Assert
.IsFalse(uri
.SchemeImplicitlyPrepended
);
53 Assert
.IsFalse(uri
.IsDiscoverySecureEndToEnd
);
57 public void CtorStringNoSchemeSecure() {
58 var uri
= new UriIdentifier("host/path", true);
59 Assert
.AreEqual("https://host/path", uri
.Uri
.AbsoluteUri
);
60 Assert
.IsTrue(uri
.IsDiscoverySecureEndToEnd
);
64 public void CtorStringHttpsSchemeSecure() {
65 var uri
= new UriIdentifier("https://host/path", true);
66 Assert
.AreEqual("https://host/path", uri
.Uri
.AbsoluteUri
);
67 Assert
.IsTrue(uri
.IsDiscoverySecureEndToEnd
);
70 [TestMethod
, ExpectedException(typeof(ArgumentException
))]
71 public void CtorStringHttpSchemeSecure() {
72 new UriIdentifier("http://host/path", true);
76 public void CtorUriHttpsSchemeSecure() {
77 var uri
= new UriIdentifier(new Uri("https://host/path"), true);
78 Assert
.AreEqual("https://host/path", uri
.Uri
.AbsoluteUri
);
79 Assert
.IsTrue(uri
.IsDiscoverySecureEndToEnd
);
82 [TestMethod
, ExpectedException(typeof(ArgumentException
))]
83 public void CtorUriHttpSchemeSecure() {
84 new UriIdentifier(new Uri("http://host/path"), true);
88 /// Verifies that the fragment is not stripped from an Identifier.
91 /// Although fragments should be stripped from user supplied identifiers,
92 /// they should NOT be stripped from claimed identifiers. So the UriIdentifier
93 /// class, which serves both identifier types, must not do the stripping.
96 public void DoesNotStripFragment() {
97 Uri original
= new Uri("http://a/b#c");
98 UriIdentifier identifier
= new UriIdentifier(original
);
99 Assert
.AreEqual(original
.Fragment
, identifier
.Uri
.Fragment
);
103 public void IsValid() {
104 Assert
.IsTrue(UriIdentifier
.IsValidUri(this.goodUri
));
105 Assert
.IsFalse(UriIdentifier
.IsValidUri(this.badUri
));
106 Assert
.IsTrue(UriIdentifier
.IsValidUri(relativeUri
), "URL lacking http:// prefix should have worked anyway.");
110 public void TrimFragment() {
111 Identifier noFragment
= UriIdentifier
.Parse("http://a/b");
112 Identifier fragment
= UriIdentifier
.Parse("http://a/b#c");
113 Assert
.AreSame(noFragment
, noFragment
.TrimFragment());
114 Assert
.AreEqual(noFragment
, fragment
.TrimFragment());
118 public void ToStringTest() {
119 Assert
.AreEqual(this.goodUri
, new UriIdentifier(this.goodUri
).ToString());
123 public void EqualsTest() {
124 Assert
.AreEqual(new UriIdentifier(this.goodUri
), new UriIdentifier(this.goodUri
));
125 // This next test is an interesting side-effect of passing off to Uri.Equals. But it's probably ok.
126 Assert
.AreEqual(new UriIdentifier(this.goodUri
), new UriIdentifier(this.goodUri
+ "#frag"));
127 Assert
.AreNotEqual(new UriIdentifier(this.goodUri
), new UriIdentifier(this.goodUri
+ "a"));
128 Assert
.AreNotEqual(null, new UriIdentifier(this.goodUri
));
129 Assert
.AreEqual(this.goodUri
, new UriIdentifier(this.goodUri
));
133 public void UnicodeTest() {
134 string unicodeUrl
= "http://nerdbank.org/opaffirmative/崎村.aspx";
135 Assert
.IsTrue(UriIdentifier
.IsValidUri(unicodeUrl
));
137 Assert
.IsTrue(UriIdentifier
.TryParse(unicodeUrl
, out id
));
138 Assert
.AreEqual("/opaffirmative/%E5%B4%8E%E6%9D%91.aspx", ((UriIdentifier
)id
).Uri
.AbsolutePath
);
139 Assert
.AreEqual(Uri
.EscapeUriString(unicodeUrl
), id
.ToString());
143 public void HtmlDiscover_11() {
144 this.DiscoverHtml("html10prov", ProtocolVersion
.V11
, null);
145 this.DiscoverHtml("html10both", ProtocolVersion
.V11
, "http://c/d");
146 this.FailDiscoverHtml("html10del");
150 public void HtmlDiscover_20() {
151 this.DiscoverHtml("html20prov", ProtocolVersion
.V20
, null);
152 this.DiscoverHtml("html20both", ProtocolVersion
.V20
, "http://c/d");
153 this.FailDiscoverHtml("html20del");
154 this.DiscoverHtml("html2010", ProtocolVersion
.V20
, "http://c/d");
155 this.DiscoverHtml("html1020", ProtocolVersion
.V20
, "http://c/d");
156 this.DiscoverHtml("html2010combinedA", ProtocolVersion
.V20
, "http://c/d");
157 this.DiscoverHtml("html2010combinedB", ProtocolVersion
.V20
, "http://c/d");
158 this.DiscoverHtml("html2010combinedC", ProtocolVersion
.V20
, "http://c/d");
159 this.FailDiscoverHtml("html20relative");
163 public void XrdsDiscoveryFromHead() {
164 this.mockResponder
.RegisterMockResponse(new Uri("http://localhost/xrds1020.xml"),
165 "application/xrds+xml", TestSupport
.LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds1020.xml"));
166 this.DiscoverXrds("XrdsReferencedInHead.html", ProtocolVersion
.V10
, null);
170 public void XrdsDiscoveryFromHttpHeader() {
171 WebHeaderCollection headers
= new WebHeaderCollection();
172 headers
.Add("X-XRDS-Location", TestSupport
.GetFullUrl("http://localhost/xrds1020.xml").AbsoluteUri
);
173 this.mockResponder
.RegisterMockResponse(new Uri("http://localhost/xrds1020.xml"), "application/xrds+xml", TestSupport
.LoadEmbeddedFile("/Discovery/xrdsdiscovery/xrds1020.xml"));
174 this.DiscoverXrds("XrdsReferencedInHttpHeader.html", ProtocolVersion
.V10
, null, headers
);
178 public void XrdsDirectDiscovery_10() {
179 this.FailDiscoverXrds("xrds-irrelevant");
180 this.DiscoverXrds("xrds10", ProtocolVersion
.V10
, null);
181 this.DiscoverXrds("xrds11", ProtocolVersion
.V11
, null);
182 this.DiscoverXrds("xrds1020", ProtocolVersion
.V10
, null);
186 public void XrdsDirectDiscovery_20() {
187 this.DiscoverXrds("xrds20", ProtocolVersion
.V20
, null);
188 this.DiscoverXrds("xrds2010a", ProtocolVersion
.V20
, null);
189 this.DiscoverXrds("xrds2010b", ProtocolVersion
.V20
, null);
193 public void NormalizeCase() {
194 // only the host name can be normalized in casing safely.
195 Identifier id
= "http://HOST:80/PaTH?KeY=VaLUE#fRag";
196 Assert
.AreEqual("http://host/PaTH?KeY=VaLUE#fRag", id
.ToString());
197 // make sure https is preserved, along with port 80, which is NON-default for https
198 id
= "https://HOST:80/PaTH?KeY=VaLUE#fRag";
199 Assert
.AreEqual("https://host:80/PaTH?KeY=VaLUE#fRag", id
.ToString());
203 public void HttpSchemePrepended() {
204 UriIdentifier id
= new UriIdentifier("www.yahoo.com");
205 Assert
.AreEqual("http://www.yahoo.com/", id
.ToString());
206 Assert
.IsTrue(id
.SchemeImplicitlyPrepended
);
209 ////[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.")]
210 public void CtorDisallowedScheme() {
211 UriIdentifier id
= new UriIdentifier(new Uri("ftp://host/path"));
212 Assert
.AreEqual("http://ftp://host/path", id
.ToString());
213 Assert
.IsTrue(id
.SchemeImplicitlyPrepended
);
217 public void DiscoveryWithRedirects() {
218 Identifier claimedId
= TestSupport
.GetMockIdentifier(TestSupport
.Scenarios
.AutoApproval
, this.mockResponder
, ProtocolVersion
.V20
);
220 // Add a couple of chained redirect pages that lead to the claimedId.
221 Uri userSuppliedUri
= TestSupport
.GetFullUrl("/someSecurePage", null, true);
222 Uri insecureMidpointUri
= TestSupport
.GetFullUrl("/insecureStop");
223 this.mockResponder
.RegisterMockRedirect(userSuppliedUri
, insecureMidpointUri
);
224 this.mockResponder
.RegisterMockRedirect(insecureMidpointUri
, new Uri(claimedId
.ToString()));
226 // don't require secure SSL discovery for this test.
227 Identifier userSuppliedIdentifier
= new UriIdentifier(userSuppliedUri
, false);
228 Assert
.AreEqual(1, userSuppliedIdentifier
.Discover(this.requestHandler
).Count());
232 public void TryRequireSslAdjustsIdentifier() {
234 // Try Parse and ctor without explicit scheme
235 var id
= Identifier
.Parse("www.yahoo.com");
236 Assert
.AreEqual("http://www.yahoo.com/", id
.ToString());
237 Assert
.IsTrue(id
.TryRequireSsl(out secureId
));
238 Assert
.IsTrue(secureId
.IsDiscoverySecureEndToEnd
);
239 Assert
.AreEqual("https://www.yahoo.com/", secureId
.ToString());
241 id
= new UriIdentifier("www.yahoo.com");
242 Assert
.AreEqual("http://www.yahoo.com/", id
.ToString());
243 Assert
.IsTrue(id
.TryRequireSsl(out secureId
));
244 Assert
.IsTrue(secureId
.IsDiscoverySecureEndToEnd
);
245 Assert
.AreEqual("https://www.yahoo.com/", secureId
.ToString());
247 // Try Parse and ctor with explicit http:// scheme
248 id
= Identifier
.Parse("http://www.yahoo.com");
249 Assert
.IsFalse(id
.TryRequireSsl(out secureId
));
250 Assert
.IsFalse(secureId
.IsDiscoverySecureEndToEnd
);
251 Assert
.AreEqual("http://www.yahoo.com/", secureId
.ToString());
252 Assert
.AreEqual(0, secureId
.Discover(this.requestHandler
).Count());
254 id
= new UriIdentifier("http://www.yahoo.com");
255 Assert
.IsFalse(id
.TryRequireSsl(out secureId
));
256 Assert
.IsFalse(secureId
.IsDiscoverySecureEndToEnd
);
257 Assert
.AreEqual("http://www.yahoo.com/", secureId
.ToString());
258 Assert
.AreEqual(0, secureId
.Discover(this.requestHandler
).Count());
262 public void DiscoverRequireSslWithSecureRedirects() {
263 Identifier claimedId
= TestSupport
.GetMockIdentifier(TestSupport
.Scenarios
.AutoApproval
, this.mockResponder
, ProtocolVersion
.V20
, true);
265 // Add a couple of chained redirect pages that lead to the claimedId.
266 // All redirects should be secure.
267 Uri userSuppliedUri
= TestSupport
.GetFullUrl("/someSecurePage", null, true);
268 Uri secureMidpointUri
= TestSupport
.GetFullUrl("/secureStop", null, true);
269 this.mockResponder
.RegisterMockRedirect(userSuppliedUri
, secureMidpointUri
);
270 this.mockResponder
.RegisterMockRedirect(secureMidpointUri
, new Uri(claimedId
.ToString()));
272 Identifier userSuppliedIdentifier
= new UriIdentifier(userSuppliedUri
, true);
273 Assert
.AreEqual(1, userSuppliedIdentifier
.Discover(this.requestHandler
).Count());
276 [TestMethod
, ExpectedException(typeof(ProtocolException
))]
277 public void DiscoverRequireSslWithInsecureRedirect() {
278 Identifier claimedId
= TestSupport
.GetMockIdentifier(TestSupport
.Scenarios
.AutoApproval
, this.mockResponder
, ProtocolVersion
.V20
, true);
280 // Add a couple of chained redirect pages that lead to the claimedId.
281 // Include an insecure HTTP jump in those redirects to verify that
282 // the ultimate endpoint is never found as a result of high security profile.
283 Uri userSuppliedUri
= TestSupport
.GetFullUrl("/someSecurePage", null, true);
284 Uri insecureMidpointUri
= TestSupport
.GetFullUrl("/insecureStop");
285 this.mockResponder
.RegisterMockRedirect(userSuppliedUri
, insecureMidpointUri
);
286 this.mockResponder
.RegisterMockRedirect(insecureMidpointUri
, new Uri(claimedId
.ToString()));
288 Identifier userSuppliedIdentifier
= new UriIdentifier(userSuppliedUri
, true);
289 userSuppliedIdentifier
.Discover(this.requestHandler
);
293 public void DiscoveryRequireSslWithInsecureXrdsInSecureHtmlHead() {
294 var insecureXrdsSource
= TestSupport
.GetMockIdentifier(TestSupport
.Scenarios
.AutoApproval
, this.mockResponder
, ProtocolVersion
.V20
, false);
295 Uri secureClaimedUri
= TestSupport
.GetFullUrl("/secureId", null, true);
297 string html
= string.Format("<html><head><meta http-equiv='X-XRDS-Location' content='{0}'/></head><body></body></html>", insecureXrdsSource
);
298 this.mockResponder
.RegisterMockResponse(secureClaimedUri
, "text/html", html
);
300 Identifier userSuppliedIdentifier
= new UriIdentifier(secureClaimedUri
, true);
301 Assert
.AreEqual(0, userSuppliedIdentifier
.Discover(this.requestHandler
).Count());
305 public void DiscoveryRequireSslWithInsecureXrdsInSecureHttpHeader() {
306 var insecureXrdsSource
= TestSupport
.GetMockIdentifier(TestSupport
.Scenarios
.AutoApproval
, this.mockResponder
, ProtocolVersion
.V20
, false);
307 Uri secureClaimedUri
= TestSupport
.GetFullUrl("/secureId", null, true);
309 string html
= "<html><head></head><body></body></html>";
310 WebHeaderCollection headers
= new WebHeaderCollection
{
311 { "X-XRDS-Location", insecureXrdsSource }
313 this.mockResponder
.RegisterMockResponse(secureClaimedUri
, secureClaimedUri
, "text/html", headers
, html
);
315 Identifier userSuppliedIdentifier
= new UriIdentifier(secureClaimedUri
, true);
316 Assert
.AreEqual(0, userSuppliedIdentifier
.Discover(this.requestHandler
).Count());
320 public void DiscoveryRequireSslWithInsecureXrdsButSecureLinkTags() {
321 var insecureXrdsSource
= TestSupport
.GetMockIdentifier(TestSupport
.Scenarios
.AutoApproval
, this.mockResponder
, ProtocolVersion
.V20
, false);
322 Uri secureClaimedUri
= TestSupport
.GetFullUrl("/secureId", null, true);
324 Identifier localIdForLinkTag
= TestSupport
.GetDelegateUrl(TestSupport
.Scenarios
.AlwaysDeny
, true);
325 string html
= string.Format(@"
327 <meta http-equiv='X-XRDS-Location' content='{0}'/> <!-- this one will be insecure and ignored -->
328 <link rel='openid2.provider' href='{1}' />
329 <link rel='openid2.local_id' href='{2}' />
330 </head><body></body></html>",
331 HttpUtility
.HtmlEncode(insecureXrdsSource
),
332 HttpUtility
.HtmlEncode(TestSupport
.GetFullUrl("/" + TestSupport
.ProviderPage
, null, true).AbsoluteUri
),
333 HttpUtility
.HtmlEncode(localIdForLinkTag
.ToString()));
334 this.mockResponder
.RegisterMockResponse(secureClaimedUri
, "text/html", html
);
336 Identifier userSuppliedIdentifier
= new UriIdentifier(secureClaimedUri
, true);
337 Assert
.AreEqual(localIdForLinkTag
, userSuppliedIdentifier
.Discover(this.requestHandler
).Single().ProviderLocalIdentifier
);
341 public void DiscoveryRequiresSslIgnoresInsecureEndpointsInXrds() {
342 var insecureEndpoint
= TestSupport
.GetServiceEndpoint(TestSupport
.Scenarios
.AutoApproval
, ProtocolVersion
.V20
, 10, false);
343 var secureEndpoint
= TestSupport
.GetServiceEndpoint(TestSupport
.Scenarios
.ApproveOnSetup
, ProtocolVersion
.V20
, 20, true);
344 UriIdentifier secureClaimedId
= new UriIdentifier(TestSupport
.GetFullUrl("/claimedId", null, true), true);
345 this.mockResponder
.RegisterMockXrdsResponse(secureClaimedId
, new ServiceEndpoint
[] { insecureEndpoint, secureEndpoint }
);
346 Assert
.AreEqual(secureEndpoint
.ProviderLocalIdentifier
, secureClaimedId
.Discover(this.requestHandler
).Single().ProviderLocalIdentifier
);
349 private void Discover(string url
, ProtocolVersion version
, Identifier expectedLocalId
, bool expectSreg
, bool useRedirect
) {
350 this.Discover(url
, version
, expectedLocalId
, expectSreg
, useRedirect
, null);
353 private void Discover(string url
, ProtocolVersion version
, Identifier expectedLocalId
, bool expectSreg
, bool useRedirect
, WebHeaderCollection headers
) {
354 Protocol protocol
= Protocol
.Lookup(version
);
355 UriIdentifier claimedId
= TestSupport
.GetFullUrl(url
);
356 UriIdentifier userSuppliedIdentifier
= TestSupport
.GetFullUrl(
357 "Discovery/htmldiscovery/redirect.aspx?target=" + url
);
358 if (expectedLocalId
== null) {
359 expectedLocalId
= claimedId
;
361 Identifier idToDiscover
= useRedirect
? userSuppliedIdentifier
: claimedId
;
364 if (url
.EndsWith("html")) {
365 contentType
= "text/html";
366 } else if (url
.EndsWith("xml")) {
367 contentType
= "application/xrds+xml";
369 throw new InvalidOperationException();
371 this.mockResponder
.RegisterMockResponse(new Uri(idToDiscover
), claimedId
, contentType
, headers
?? new WebHeaderCollection(), TestSupport
.LoadEmbeddedFile(url
));
373 ServiceEndpoint se
= idToDiscover
.Discover(this.requestHandler
).FirstOrDefault();
374 Assert
.IsNotNull(se
, url
+ " failed to be discovered.");
375 Assert
.AreSame(protocol
, se
.Protocol
);
376 Assert
.AreEqual(claimedId
, se
.ClaimedIdentifier
);
377 Assert
.AreEqual(expectedLocalId
, se
.ProviderLocalIdentifier
);
378 Assert
.AreEqual(expectSreg
? 2 : 1, se
.ProviderSupportedServiceTypeUris
.Length
);
379 Assert
.IsTrue(Array
.IndexOf(se
.ProviderSupportedServiceTypeUris
, protocol
.ClaimedIdentifierServiceTypeURI
) >= 0);
381 // TODO: re-enable this line once extensions support is added back in.
382 ////Assert.AreEqual(expectSreg, se.IsExtensionSupported(new ClaimsRequest()));
385 private void DiscoverXrds(string page
, ProtocolVersion version
, Identifier expectedLocalId
) {
386 this.DiscoverXrds(page
, version
, expectedLocalId
, null);
389 private void DiscoverXrds(string page
, ProtocolVersion version
, Identifier expectedLocalId
, WebHeaderCollection headers
) {
390 if (!page
.Contains(".")) page
+= ".xml";
391 this.Discover("/Discovery/xrdsdiscovery/" + page
, version
, expectedLocalId
, true, false, headers
);
392 this.Discover("/Discovery/xrdsdiscovery/" + page
, version
, expectedLocalId
, true, true, headers
);
395 private void DiscoverHtml(string page
, ProtocolVersion version
, Identifier expectedLocalId
, bool useRedirect
) {
396 this.Discover("/Discovery/htmldiscovery/" + page
, version
, expectedLocalId
, false, useRedirect
);
399 private void DiscoverHtml(string scenario
, ProtocolVersion version
, Identifier expectedLocalId
) {
400 string page
= scenario
+ ".html";
401 this.DiscoverHtml(page
, version
, expectedLocalId
, false);
402 this.DiscoverHtml(page
, version
, expectedLocalId
, true);
405 private void FailDiscover(string url
) {
406 UriIdentifier userSuppliedId
= TestSupport
.GetFullUrl(url
);
408 this.mockResponder
.RegisterMockResponse(new Uri(userSuppliedId
), userSuppliedId
, "text/html", TestSupport
.LoadEmbeddedFile(url
));
410 Assert
.AreEqual(0, userSuppliedId
.Discover(this.requestHandler
).Count()); // ... but that no endpoint info is discoverable
413 private void FailDiscoverHtml(string scenario
) {
414 this.FailDiscover("/Discovery/htmldiscovery/" + scenario
+ ".html");
417 private void FailDiscoverXrds(string scenario
) {
418 this.FailDiscover("/Discovery/xrdsdiscovery/" + scenario
+ ".xml");