summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Messages/CheckIdRequestTests.cs55
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/RealmTests.cs41
-rw-r--r--src/DotNetOpenAuth/GlobalSuppressions.cs4
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs22
-rw-r--r--src/DotNetOpenAuth/OAuth/ServiceProvider.cs4
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdMessageFactory.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/Identifier.cs90
-rw-r--r--src/DotNetOpenAuth/OpenId/NoDiscoveryIdentifier.cs87
-rw-r--r--src/DotNetOpenAuth/OpenId/Realm.cs367
-rw-r--r--src/DotNetOpenAuth/OpenId/UriIdentifier.cs308
-rw-r--r--src/DotNetOpenAuth/OpenId/XriIdentifier.cs234
-rw-r--r--src/DotNetOpenAuth/UriUtil.cs7
-rw-r--r--src/DotNetOpenAuth/Util.cs22
13 files changed, 843 insertions, 400 deletions
diff --git a/src/DotNetOpenAuth.Test/OpenId/Messages/CheckIdRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Messages/CheckIdRequestTests.cs
index 0f970f9..dd7b3d9 100644
--- a/src/DotNetOpenAuth.Test/OpenId/Messages/CheckIdRequestTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/Messages/CheckIdRequestTests.cs
@@ -9,42 +9,41 @@ namespace DotNetOpenAuth.Test.OpenId.Messages {
using System.Collections.Generic;
using System.Linq;
using System.Text;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
- using DotNetOpenAuth.OpenId.Messages;
- using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.Messages;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class CheckIdRequestTests : OpenIdTestBase {
- private Uri ProviderEndpoint;
+ private Uri providerEndpoint;
private CheckIdRequest immediatev1;
private CheckIdRequest setupv1;
private CheckIdRequest immediatev2;
private CheckIdRequest setupv2;
-
[TestInitialize]
public override void SetUp() {
base.SetUp();
- ProviderEndpoint = new Uri("http://host");
+ this.providerEndpoint = new Uri("http://host");
- immediatev1 = new CheckIdRequest(Protocol.V11.Version, ProviderEndpoint, true);
- setupv1 = new CheckIdRequest(Protocol.V11.Version, ProviderEndpoint, false);
+ this.immediatev1 = new CheckIdRequest(Protocol.V11.Version, this.providerEndpoint, true);
+ this.setupv1 = new CheckIdRequest(Protocol.V11.Version, this.providerEndpoint, false);
- immediatev2 = new CheckIdRequest(Protocol.V20.Version, ProviderEndpoint, true);
- setupv2 = new CheckIdRequest(Protocol.V20.Version, ProviderEndpoint, false);
+ this.immediatev2 = new CheckIdRequest(Protocol.V20.Version, this.providerEndpoint, true);
+ this.setupv2 = new CheckIdRequest(Protocol.V20.Version, this.providerEndpoint, false);
// Prepare all message versions so that they SHOULD be valid by default.
// In particular, V1 messages require ReturnTo.
- immediatev1.ReturnTo = new Uri("http://returnto/");
- setupv1.ReturnTo = new Uri("http://returnto/");
+ this.immediatev1.ReturnTo = new Uri("http://returnto/");
+ this.setupv1.ReturnTo = new Uri("http://returnto/");
try {
- immediatev1.EnsureValidMessage();
- setupv1.EnsureValidMessage();
- immediatev2.EnsureValidMessage();
- setupv2.EnsureValidMessage();
+ this.immediatev1.EnsureValidMessage();
+ this.setupv1.EnsureValidMessage();
+ this.immediatev2.EnsureValidMessage();
+ this.setupv2.EnsureValidMessage();
} catch (ProtocolException ex) {
Assert.Inconclusive("All messages ought to be valid before tests run, but got: {0}", ex.Message);
}
@@ -56,8 +55,8 @@ namespace DotNetOpenAuth.Test.OpenId.Messages {
/// </summary>
[TestMethod, ExpectedException(typeof(ProtocolException))]
public void ClaimedIdentifierWithoutIdentity() {
- setupv2.ClaimedIdentifier = "http://andrew.arnott.myopenid.com/";
- setupv2.EnsureValidMessage();
+ this.setupv2.ClaimedIdentifier = "http://andrew.arnott.myopenid.com/";
+ this.setupv2.EnsureValidMessage();
}
/// <summary>
@@ -66,8 +65,8 @@ namespace DotNetOpenAuth.Test.OpenId.Messages {
/// </summary>
[TestMethod, ExpectedException(typeof(ProtocolException))]
public void LocalIdentifierWithoutClaimedIdentifier() {
- setupv2.LocalIdentifier = "http://andrew.arnott.myopenid.com/";
- setupv2.EnsureValidMessage();
+ this.setupv2.LocalIdentifier = "http://andrew.arnott.myopenid.com/";
+ this.setupv2.EnsureValidMessage();
}
/// <summary>
@@ -76,8 +75,8 @@ namespace DotNetOpenAuth.Test.OpenId.Messages {
/// </summary>
[TestMethod]
public void LocalIdentifierWithoutClaimedIdentifierV1() {
- setupv1.LocalIdentifier = "http://andrew.arnott.myopenid.com/";
- setupv1.EnsureValidMessage();
+ this.setupv1.LocalIdentifier = "http://andrew.arnott.myopenid.com/";
+ this.setupv1.EnsureValidMessage();
}
/// <summary>
@@ -86,9 +85,9 @@ namespace DotNetOpenAuth.Test.OpenId.Messages {
/// </summary>
[TestMethod, ExpectedException(typeof(ProtocolException))]
public void RealmReturnToMismatchV2() {
- setupv2.Realm = "http://somehost/";
- setupv2.ReturnTo = new Uri("http://someotherhost/");
- setupv2.EnsureValidMessage();
+ this.setupv2.Realm = "http://somehost/";
+ this.setupv2.ReturnTo = new Uri("http://someotherhost/");
+ this.setupv2.EnsureValidMessage();
}
/// <summary>
@@ -97,9 +96,9 @@ namespace DotNetOpenAuth.Test.OpenId.Messages {
/// </summary>
[TestMethod, ExpectedException(typeof(ProtocolException))]
public void RealmReturnToMismatchV1() {
- setupv1.Realm = "http://somehost/";
- setupv1.ReturnTo = new Uri("http://someotherhost/");
- setupv1.EnsureValidMessage();
+ this.setupv1.Realm = "http://somehost/";
+ this.setupv1.ReturnTo = new Uri("http://someotherhost/");
+ this.setupv1.EnsureValidMessage();
}
}
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/RealmTests.cs b/src/DotNetOpenAuth.Test/OpenId/RealmTests.cs
index 5d59ded..e062e12 100644
--- a/src/DotNetOpenAuth.Test/OpenId/RealmTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/RealmTests.cs
@@ -4,18 +4,15 @@
// </copyright>
//-----------------------------------------------------------------------
-namespace DotNetOpenAuth.Test
-{
+namespace DotNetOpenAuth.Test {
using System;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
using DotNetOpenAuth.OpenId;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
- public class RealmTests
- {
+ public class RealmTests {
[TestMethod]
- public void ValidRealmsTest()
- {
+ public void ValidRealmsTest() {
// Just create these. If any are determined to be invalid,
// an exception should be thrown that would fail this test.
new Realm("http://www.myopenid.com");
@@ -31,8 +28,7 @@ namespace DotNetOpenAuth.Test
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
- public void InvalidRealmNullString()
- {
+ public void InvalidRealmNullString() {
new Realm((string)null);
}
@@ -44,29 +40,25 @@ namespace DotNetOpenAuth.Test
[TestMethod]
[ExpectedException(typeof(UriFormatException))]
- public void InvalidRealmEmpty()
- {
+ public void InvalidRealmEmpty() {
new Realm(string.Empty);
}
[TestMethod]
[ExpectedException(typeof(UriFormatException))]
- public void InvalidRealmBadProtocol()
- {
+ public void InvalidRealmBadProtocol() {
new Realm("asdf://www.microsoft.com/");
}
[TestMethod]
[ExpectedException(typeof(UriFormatException))]
- public void InvalidRealmNoScheme()
- {
+ public void InvalidRealmNoScheme() {
new Realm("www.guy.com");
}
[TestMethod]
[ExpectedException(typeof(UriFormatException))]
- public void InvalidRealmBadWildcard1()
- {
+ public void InvalidRealmBadWildcard1() {
new Realm("http://*www.my.com");
}
@@ -84,21 +76,18 @@ namespace DotNetOpenAuth.Test
[TestMethod]
[ExpectedException(typeof(UriFormatException))]
- public void InvalidRealmTwoWildcards1()
- {
+ public void InvalidRealmTwoWildcards1() {
new Realm("http://**.my.com");
}
[TestMethod]
[ExpectedException(typeof(UriFormatException))]
- public void InvalidRealmTwoWildcards2()
- {
+ public void InvalidRealmTwoWildcards2() {
new Realm("http://*.*.my.com");
}
[TestMethod]
- public void IsSaneTest()
- {
+ public void IsSaneTest() {
Assert.IsTrue(new Realm("http://www.myopenid.com").IsSane);
Assert.IsTrue(new Realm("http://myopenid.com").IsSane);
Assert.IsTrue(new Realm("http://localhost").IsSane);
@@ -110,8 +99,7 @@ namespace DotNetOpenAuth.Test
}
[TestMethod]
- public void IsUrlWithinRealmTests()
- {
+ public void IsUrlWithinRealmTests() {
/*
* The openid.return_to URL MUST descend from the openid.trust_root, or the
* Identity Provider SHOULD return an error. Namely, the URL scheme and port
@@ -207,8 +195,7 @@ namespace DotNetOpenAuth.Test
}
[TestMethod]
- public void EqualsTest()
- {
+ public void EqualsTest() {
Realm testRealm1a = new Realm("http://www.yahoo.com");
Realm testRealm1b = new Realm("http://www.yahoo.com");
Realm testRealm2 = new Realm("http://www.yahoo.com/b");
diff --git a/src/DotNetOpenAuth/GlobalSuppressions.cs b/src/DotNetOpenAuth/GlobalSuppressions.cs
index aed15eb..4113c73 100644
--- a/src/DotNetOpenAuth/GlobalSuppressions.cs
+++ b/src/DotNetOpenAuth/GlobalSuppressions.cs
@@ -25,3 +25,7 @@
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "Mono.Math.BigInteger.#op_Multiply(Mono.Math.BigInteger,Mono.Math.BigInteger)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "sign", Scope = "member", Target = "Mono.Math.BigInteger.#.ctor(Mono.Math.BigInteger+Sign,System.UInt32)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "notUsed", Scope = "member", Target = "Mono.Math.BigInteger.#isProbablePrime(System.Int32)")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "returnto", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "openid", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "claimedid", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")]
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Xri", Scope = "type", Target = "DotNetOpenAuth.OpenId.XriIdentifier")]
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs
index ae52679..94812d8 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs
@@ -197,6 +197,28 @@ namespace DotNetOpenAuth.Messaging.Reflection {
internal Version Version { get { return this.version; } }
/// <summary>
+ /// Implements the operator ==.
+ /// </summary>
+ /// <param name="first">The first object to compare.</param>
+ /// <param name="second">The second object to compare.</param>
+ /// <returns>The result of the operator.</returns>
+ public static bool operator ==(MessageTypeAndVersion first, MessageTypeAndVersion second) {
+ // structs cannot be null, so this is safe
+ return first.Equals(second);
+ }
+
+ /// <summary>
+ /// Implements the operator !=.
+ /// </summary>
+ /// <param name="first">The first object to compare.</param>
+ /// <param name="second">The second object to compare.</param>
+ /// <returns>The result of the operator.</returns>
+ public static bool operator !=(MessageTypeAndVersion first, MessageTypeAndVersion second) {
+ // structs cannot be null, so this is safe
+ return !first.Equals(second);
+ }
+
+ /// <summary>
/// Indicates whether this instance and a specified object are equal.
/// </summary>
/// <param name="obj">Another object to compare to.</param>
diff --git a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs
index 824aa20..2ede84b 100644
--- a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs
+++ b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs
@@ -228,9 +228,7 @@ namespace DotNetOpenAuth.OAuth {
/// Null if the Consumer did not request a callback.
/// </returns>
public UserAuthorizationResponse PrepareAuthorizationResponse(UserAuthorizationRequest request) {
- if (request == null) {
- throw new ArgumentNullException("request");
- }
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
if (request.Callback != null) {
var authorization = new UserAuthorizationResponse(request.Callback) {
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdMessageFactory.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdMessageFactory.cs
index 6d564b5..b14ac04 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdMessageFactory.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdMessageFactory.cs
@@ -99,7 +99,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
message = new AssociateUnencryptedResponse(associateUnencryptedRequest);
}
- // TODO: recognize more message types here
+ //// TODO: recognize more message types here
if (message != null) {
message.SetAsIncoming();
diff --git a/src/DotNetOpenAuth/OpenId/Identifier.cs b/src/DotNetOpenAuth/OpenId/Identifier.cs
index 2eff359..ae40d4e 100644
--- a/src/DotNetOpenAuth/OpenId/Identifier.cs
+++ b/src/DotNetOpenAuth/OpenId/Identifier.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OpenId {
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+ using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.RelyingParty;
/// <summary>
@@ -17,63 +18,82 @@ namespace DotNetOpenAuth.OpenId {
[Serializable]
public abstract class Identifier {
/// <summary>
- /// Constructs an <see cref="Identifier"/>.
+ /// Initializes a new instance of the <see cref="Identifier"/> class.
/// </summary>
/// <param name="isDiscoverySecureEndToEnd">
/// Whether the derived class is prepared to guarantee end-to-end discovery
/// and initial redirect for authentication is performed using SSL.
/// </param>
protected Identifier(bool isDiscoverySecureEndToEnd) {
- IsDiscoverySecureEndToEnd = isDiscoverySecureEndToEnd;
+ this.IsDiscoverySecureEndToEnd = isDiscoverySecureEndToEnd;
}
/// <summary>
- /// Whether this Identifier will ensure SSL is used throughout the discovery phase
- /// and initial redirect of authentication.
+ /// Gets a value indicating whether this Identifier will ensure SSL is
+ /// used throughout the discovery phase and initial redirect of authentication.
/// </summary>
/// <remarks>
- /// If this is False, a value of True may be obtained by calling <see cref="TryRequireSsl"/>.
+ /// If this is <c>false</c>, a value of <c>true</c> may be obtained by calling
+ /// <see cref="TryRequireSsl"/>.
/// </remarks>
protected internal bool IsDiscoverySecureEndToEnd { get; private set; }
/// <summary>
/// Converts the string representation of an Identifier to its strong type.
/// </summary>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates"), SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads")]
+ /// <param name="identifier">The identifier.</param>
+ /// <returns>The particular Identifier instance to represent the value given.</returns>
+ [SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates", Justification = "Our named alternate is Parse.")]
+ [SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads", Justification = "TODO")]
public static implicit operator Identifier(string identifier) {
- if (identifier == null) return null;
+ if (identifier == null) {
+ return null;
+ }
return Parse(identifier);
}
+
/// <summary>
- /// Returns a strongly-typed Identifier for a given Uri.
+ /// Converts a given Uri to a strongly-typed Identifier.
/// </summary>
- [SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates")]
+ /// <param name="identifier">The identifier to convert.</param>
+ /// <returns>The result of the conversion.</returns>
+ [SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates", Justification = "TODO")]
public static implicit operator Identifier(Uri identifier) {
- if (identifier == null) return null;
+ if (identifier == null) {
+ return null;
+ }
return new UriIdentifier(identifier);
}
+
/// <summary>
/// Converts an Identifier to its string representation.
/// </summary>
- [SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates")]
+ /// <param name="identifier">The identifier to convert to a string.</param>
+ /// <returns>The result of the conversion.</returns>
+ [SuppressMessage("Microsoft.Usage", "CA2225:OperatorOverloadsHaveNamedAlternates", Justification = "TODO")]
public static implicit operator string(Identifier identifier) {
- if (identifier == null) return null;
+ if (identifier == null) {
+ return null;
+ }
return identifier.ToString();
}
+
/// <summary>
/// Parses an identifier string and automatically determines
/// whether it is an XRI or URI.
/// </summary>
/// <param name="identifier">Either a URI or XRI identifier.</param>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings")]
+ /// <returns>An <see cref="Identifier"/> instance for the given value.</returns>
+ [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "TODO")]
public static Identifier Parse(string identifier) {
- if (string.IsNullOrEmpty(identifier)) throw new ArgumentNullException("identifier");
+ ErrorUtilities.VerifyArgumentNotNull(identifier, "identifier");
if (XriIdentifier.IsValidXri(identifier)) {
return new XriIdentifier(identifier);
} else {
return new UriIdentifier(identifier);
}
}
+
/// <summary>
/// Attempts to parse a string for an OpenId Identifier.
/// </summary>
@@ -91,10 +111,15 @@ namespace DotNetOpenAuth.OpenId {
return false;
}
}
+
/// <summary>
- /// Gets whether a given string represents a valid Identifier format.
+ /// Checks the validity of a given string representation of some Identifier.
/// </summary>
- [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings")]
+ /// <param name="identifier">The identifier.</param>
+ /// <returns>
+ /// <c>true</c> if the specified identifier is valid; otherwise, <c>false</c>.
+ /// </returns>
+ [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "TODO")]
public static bool IsValid(string identifier) {
return XriIdentifier.IsValidXri(identifier) || UriIdentifier.IsValidUri(identifier);
}
@@ -112,27 +137,48 @@ namespace DotNetOpenAuth.OpenId {
/// <summary>
/// Tests equality between two <see cref="Identifier"/>s.
/// </summary>
+ /// <param name="id1">The first Identifier.</param>
+ /// <param name="id2">The second Identifier.</param>
+ /// <returns>
+ /// <c>true</c> if the two instances should be considered equal; <c>false</c> otherwise.
+ /// </returns>
public static bool operator ==(Identifier id1, Identifier id2) {
- if ((object)id1 == null ^ (object)id2 == null) return false;
- if ((object)id1 == null) return true;
- return id1.Equals(id2);
+ return id1.EqualsNullSafe(id2);
}
+
/// <summary>
/// Tests inequality between two <see cref="Identifier"/>s.
/// </summary>
+ /// <param name="id1">The first Identifier.</param>
+ /// <param name="id2">The second Identifier.</param>
+ /// <returns>
+ /// <c>true</c> if the two instances should be considered unequal; <c>false</c> if they are equal.
+ /// </returns>
public static bool operator !=(Identifier id1, Identifier id2) {
- return !(id1 == id2);
+ return !id1.EqualsNullSafe(id2);
}
+
/// <summary>
/// Tests equality between two <see cref="Identifier"/>s.
/// </summary>
+ /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
+ /// <returns>
+ /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
+ /// </returns>
+ /// <exception cref="T:System.NullReferenceException">
+ /// The <paramref name="obj"/> parameter is null.
+ /// </exception>
public override bool Equals(object obj) {
Debug.Fail("This should be overridden in every derived class.");
return base.Equals(obj);
}
+
/// <summary>
/// Gets the hash code for an <see cref="Identifier"/> for storage in a hashtable.
/// </summary>
+ /// <returns>
+ /// A hash code for the current <see cref="T:System.Object"/>.
+ /// </returns>
public override int GetHashCode() {
Debug.Fail("This should be overridden in every derived class.");
return base.GetHashCode();
@@ -140,9 +186,11 @@ namespace DotNetOpenAuth.OpenId {
/// <summary>
/// Returns an <see cref="Identifier"/> that has no URI fragment.
- /// Quietly returns the original <see cref="Identifier"/> if it is not
+ /// Quietly returns the original <see cref="Identifier"/> if it is not
/// a <see cref="UriIdentifier"/> or no fragment exists.
/// </summary>
+ /// <returns>A new <see cref="Identifier"/> instance if there was a
+ /// fragment to remove, otherwise this same instance..</returns>
internal abstract Identifier TrimFragment();
/// <summary>
diff --git a/src/DotNetOpenAuth/OpenId/NoDiscoveryIdentifier.cs b/src/DotNetOpenAuth/OpenId/NoDiscoveryIdentifier.cs
index 9a6d0ac..a6f1384 100644
--- a/src/DotNetOpenAuth/OpenId/NoDiscoveryIdentifier.cs
+++ b/src/DotNetOpenAuth/OpenId/NoDiscoveryIdentifier.cs
@@ -1,16 +1,31 @@
-namespace DotNetOpenAuth.OpenId {
+//-----------------------------------------------------------------------
+// <copyright file="NoDiscoveryIdentifier.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId {
using System;
using System.Collections.Generic;
+ using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.RelyingParty;
/// <summary>
/// Wraps an existing Identifier and prevents it from performing discovery.
/// </summary>
- class NoDiscoveryIdentifier : Identifier {
- Identifier wrappedIdentifier;
+ internal class NoDiscoveryIdentifier : Identifier {
+ /// <summary>
+ /// The wrapped identifier.
+ /// </summary>
+ private Identifier wrappedIdentifier;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NoDiscoveryIdentifier"/> class.
+ /// </summary>
+ /// <param name="wrappedIdentifier">The ordinary Identifier whose discovery is being masked.</param>
internal NoDiscoveryIdentifier(Identifier wrappedIdentifier)
: base(false) {
- if (wrappedIdentifier == null) throw new ArgumentNullException("wrappedIdentifier");
+ ErrorUtilities.VerifyArgumentNotNull(wrappedIdentifier, "wrappedIdentifier");
this.wrappedIdentifier = wrappedIdentifier;
}
@@ -20,25 +35,67 @@
return new ServiceEndpoint[0];
}
#endif
-
- internal override Identifier TrimFragment() {
- return new NoDiscoveryIdentifier(wrappedIdentifier.TrimFragment());
+ /// <summary>
+ /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
+ /// </returns>
+ public override string ToString() {
+ return this.wrappedIdentifier.ToString();
}
- internal override bool TryRequireSsl(out Identifier secureIdentifier) {
- return wrappedIdentifier.TryRequireSsl(out secureIdentifier);
+ /// <summary>
+ /// Tests equality between two <see cref="Identifier"/>s.
+ /// </summary>
+ /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
+ /// <returns>
+ /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
+ /// </returns>
+ /// <exception cref="T:System.NullReferenceException">
+ /// The <paramref name="obj"/> parameter is null.
+ /// </exception>
+ public override bool Equals(object obj) {
+ return this.wrappedIdentifier.Equals(obj);
}
- public override string ToString() {
- return wrappedIdentifier.ToString();
+ /// <summary>
+ /// Gets the hash code for an <see cref="Identifier"/> for storage in a hashtable.
+ /// </summary>
+ /// <returns>
+ /// A hash code for the current <see cref="T:System.Object"/>.
+ /// </returns>
+ public override int GetHashCode() {
+ return this.wrappedIdentifier.GetHashCode();
}
- public override bool Equals(object obj) {
- return wrappedIdentifier.Equals(obj);
+ /// <summary>
+ /// Returns an <see cref="Identifier"/> that has no URI fragment.
+ /// Quietly returns the original <see cref="Identifier"/> if it is not
+ /// a <see cref="UriIdentifier"/> or no fragment exists.
+ /// </summary>
+ /// <returns>
+ /// A new <see cref="Identifier"/> instance if there was a
+ /// fragment to remove, otherwise this same instance..
+ /// </returns>
+ internal override Identifier TrimFragment() {
+ return new NoDiscoveryIdentifier(this.wrappedIdentifier.TrimFragment());
}
- public override int GetHashCode() {
- return wrappedIdentifier.GetHashCode();
+ /// <summary>
+ /// Converts a given identifier to its secure equivalent.
+ /// UriIdentifiers originally created with an implied HTTP scheme change to HTTPS.
+ /// Discovery is made to require SSL for the entire resolution process.
+ /// </summary>
+ /// <param name="secureIdentifier">The newly created secure identifier.
+ /// If the conversion fails, <paramref name="secureIdentifier"/> retains
+ /// <i>this</i> identifiers identity, but will never discover any endpoints.</param>
+ /// <returns>
+ /// True if the secure conversion was successful.
+ /// False if the Identifier was originally created with an explicit HTTP scheme.
+ /// </returns>
+ internal override bool TryRequireSsl(out Identifier secureIdentifier) {
+ return this.wrappedIdentifier.TryRequireSsl(out secureIdentifier);
}
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Realm.cs b/src/DotNetOpenAuth/OpenId/Realm.cs
index ff38291..4b0266e 100644
--- a/src/DotNetOpenAuth/OpenId/Realm.cs
+++ b/src/DotNetOpenAuth/OpenId/Realm.cs
@@ -12,6 +12,7 @@ namespace DotNetOpenAuth.OpenId {
using System.Globalization;
using System.Text.RegularExpressions;
using System.Xml;
+ using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.Provider;
/// <summary>
@@ -23,113 +24,113 @@ namespace DotNetOpenAuth.OpenId {
/// </remarks>
public class Realm {
/// <summary>
- /// Implicitly converts the string-form of a URI to a <see cref="Realm"/> object.
+ /// A regex used to detect a wildcard that is being used in the realm.
/// </summary>
- [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings")]
- [SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads")]
- public static implicit operator Realm(string uri) {
- return uri != null ? new Realm(uri) : null;
- }
+ private const string WildcardDetectionPattern = @"^(\w+://)\*\.";
/// <summary>
- /// Implicitly converts a <see cref="Uri"/> to a <see cref="Realm"/> object.
+ /// A (more or less) comprehensive list of top-level (i.e. ".com") domains,
+ /// for use by <see cref="IsSane"/> in order to disallow overly-broad realms
+ /// that allow all web sites ending with '.com', for example.
/// </summary>
- [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings")]
- public static implicit operator Realm(Uri uri) {
- return uri != null ? new Realm(uri.AbsoluteUri) : null;
- }
+ private static readonly string[] topLevelDomains = { "com", "edu", "gov", "int", "mil", "net", "org", "biz", "info", "name", "museum", "coop", "aero", "ac", "ad", "ae",
+ "af", "ag", "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au", "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi", "bj",
+ "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by", "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl", "cm", "cn", "co", "cr",
+ "cu", "cv", "cx", "cy", "cz", "de", "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er", "es", "et", "fi", "fj", "fk", "fm", "fo",
+ "fr", "ga", "gd", "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq", "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
+ "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir", "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki", "km", "kn", "kp",
+ "kr", "kw", "ky", "kz", "la", "lb", "lc", "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc", "md", "mg", "mh", "mk", "ml", "mm",
+ "mn", "mo", "mp", "mq", "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na", "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr",
+ "nu", "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm", "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru", "rw", "sa",
+ "sb", "sc", "sd", "se", "sg", "sh", "si", "sj", "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz", "tc", "td", "tf", "tg", "th",
+ "tj", "tk", "tm", "tn", "to", "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um", "us", "uy", "uz", "va", "vc", "ve", "vg", "vi",
+ "vn", "vu", "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw" };
/// <summary>
- /// Implicitly converts a <see cref="Realm"/> object to its <see cref="String"/> form.
+ /// The Uri of the realm, with the wildcard (if any) removed.
/// </summary>
- public static implicit operator string(Realm realm) {
- return realm != null ? realm.ToString() : null;
- }
+ private Uri uri;
/// <summary>
- /// Instantiates a <see cref="Realm"/> from its string representation.
+ /// Initializes a new instance of the <see cref="Realm"/> class.
/// </summary>
- [SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads"), SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
+ /// <param name="realmUrl">The realm URL to use in the new instance.</param>
+ [SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads", Justification = "TODO")]
public Realm(string realmUrl) {
- if (realmUrl == null) throw new ArgumentNullException("realmUrl");
- DomainWildcard = Regex.IsMatch(realmUrl, wildcardDetectionPattern);
- uri = new Uri(Regex.Replace(realmUrl, wildcardDetectionPattern, m => m.Groups[1].Value));
- if (!uri.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase) &&
- !uri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase))
- throw new UriFormatException(string.Format(CultureInfo.CurrentCulture,
- OpenIdStrings.InvalidScheme, uri.Scheme));
+ ErrorUtilities.VerifyArgumentNotNull(realmUrl, "realmUrl");
+ this.DomainWildcard = Regex.IsMatch(realmUrl, WildcardDetectionPattern);
+ this.uri = new Uri(Regex.Replace(realmUrl, WildcardDetectionPattern, m => m.Groups[1].Value));
+ if (!this.uri.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase) &&
+ !this.uri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase)) {
+ throw new UriFormatException(
+ string.Format(CultureInfo.CurrentCulture, OpenIdStrings.InvalidScheme, this.uri.Scheme));
+ }
}
/// <summary>
- /// Instantiates a <see cref="Realm"/> from its <see cref="Uri"/> representation.
+ /// Initializes a new instance of the <see cref="Realm"/> class.
/// </summary>
+ /// <param name="realmUrl">The realm URL of the Relying Party.</param>
public Realm(Uri realmUrl) {
- if (realmUrl == null) throw new ArgumentNullException("realmUrl");
- uri = realmUrl;
- if (!uri.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase) &&
- !uri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase))
- throw new UriFormatException(string.Format(CultureInfo.CurrentCulture,
- OpenIdStrings.InvalidScheme, uri.Scheme));
+ ErrorUtilities.VerifyArgumentNotNull(realmUrl, "realmUrl");
+ this.uri = realmUrl;
+ if (!this.uri.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase) &&
+ !this.uri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase)) {
+ throw new UriFormatException(
+ string.Format(CultureInfo.CurrentCulture, OpenIdStrings.InvalidScheme, this.uri.Scheme));
+ }
}
/// <summary>
- /// Instantiates a <see cref="Realm"/> from its <see cref="UriBuilder"/> representation.
+ /// Initializes a new instance of the <see cref="Realm"/> class.
/// </summary>
+ /// <param name="realmUriBuilder">The realm URI builder.</param>
/// <remarks>
- /// This is useful because UriBuilder can construct a host with a wildcard
+ /// This is useful because UriBuilder can construct a host with a wildcard
/// in the Host property, but once there it can't be converted to a Uri.
/// </remarks>
internal Realm(UriBuilder realmUriBuilder)
- : this(safeUriBuilderToString(realmUriBuilder)) { }
- static string safeUriBuilderToString(UriBuilder realmUriBuilder) {
- if (realmUriBuilder == null) throw new ArgumentNullException("realmUriBuilder");
- // Note: we MUST use ToString. Uri property throws if wildcard is present.
- return realmUriBuilder.ToString();
- }
-
- Uri uri;
- const string wildcardDetectionPattern = @"^(\w+://)\*\.";
+ : this(SafeUriBuilderToString(realmUriBuilder)) { }
/// <summary>
- /// Whether a '*.' prefix to the hostname is used in the realm to allow
- /// subdomains or hosts to be added to the URL.
+ /// Gets a value indicating whether a '*.' prefix to the hostname is
+ /// used in the realm to allow subdomains or hosts to be added to the URL.
/// </summary>
public bool DomainWildcard { get; private set; }
-
+
/// <summary>
/// Gets the host component of this instance.
/// </summary>
- public string Host { get { return uri.Host; } }
-
+ public string Host { get { return this.uri.Host; } }
+
/// <summary>
/// Gets the scheme name for this URI.
/// </summary>
- public string Scheme { get { return uri.Scheme; } }
-
+ public string Scheme { get { return this.uri.Scheme; } }
+
/// <summary>
/// Gets the port number of this URI.
/// </summary>
- public int Port { get { return uri.Port; } }
-
+ public int Port { get { return this.uri.Port; } }
+
/// <summary>
/// Gets the absolute path of the URI.
/// </summary>
- public string AbsolutePath { get { return uri.AbsolutePath; } }
-
+ public string AbsolutePath { get { return this.uri.AbsolutePath; } }
+
/// <summary>
/// Gets the System.Uri.AbsolutePath and System.Uri.Query properties separated
/// by a question mark (?).
/// </summary>
- public string PathAndQuery { get { return uri.PathAndQuery; } }
-
+ public string PathAndQuery { get { return this.uri.PathAndQuery; } }
+
/// <summary>
/// Gets the realm URL. If the realm includes a wildcard, it is not included here.
/// </summary>
- internal Uri NoWildcardUri { get { return uri; } }
-
+ internal Uri NoWildcardUri { get { return this.uri; } }
+
/// <summary>
- /// Produces the Realm URL. If the realm URL had a wildcard in it,
- /// the wildcard is replaced with a "www." prefix.
+ /// Gets the Realm discovery URL, where the wildcard (if present) is replaced with "www.".
/// </summary>
/// <remarks>
/// See OpenID 2.0 spec section 9.2.1 for the explanation on the addition of
@@ -137,31 +138,18 @@ namespace DotNetOpenAuth.OpenId {
/// </remarks>
internal Uri UriWithWildcardChangedToWww {
get {
- if (DomainWildcard) {
- UriBuilder builder = new UriBuilder(NoWildcardUri);
+ if (this.DomainWildcard) {
+ UriBuilder builder = new UriBuilder(this.NoWildcardUri);
builder.Host = "www." + builder.Host;
return builder.Uri;
} else {
- return NoWildcardUri;
+ return this.NoWildcardUri;
}
}
}
- static string[] topLevelDomains = { "com", "edu", "gov", "int", "mil", "net", "org", "biz", "info", "name", "museum", "coop", "aero", "ac", "ad", "ae",
- "af", "ag", "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at", "au", "aw", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi", "bj",
- "bm", "bn", "bo", "br", "bs", "bt", "bv", "bw", "by", "bz", "ca", "cc", "cd", "cf", "cg", "ch", "ci", "ck", "cl", "cm", "cn", "co", "cr",
- "cu", "cv", "cx", "cy", "cz", "de", "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "eh", "er", "es", "et", "fi", "fj", "fk", "fm", "fo",
- "fr", "ga", "gd", "ge", "gf", "gg", "gh", "gi", "gl", "gm", "gn", "gp", "gq", "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr",
- "ht", "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir", "is", "it", "je", "jm", "jo", "jp", "ke", "kg", "kh", "ki", "km", "kn", "kp",
- "kr", "kw", "ky", "kz", "la", "lb", "lc", "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc", "md", "mg", "mh", "mk", "ml", "mm",
- "mn", "mo", "mp", "mq", "mr", "ms", "mt", "mu", "mv", "mw", "mx", "my", "mz", "na", "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr",
- "nu", "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm", "pn", "pr", "ps", "pt", "pw", "py", "qa", "re", "ro", "ru", "rw", "sa",
- "sb", "sc", "sd", "se", "sg", "sh", "si", "sj", "sk", "sl", "sm", "sn", "so", "sr", "st", "sv", "sy", "sz", "tc", "td", "tf", "tg", "th",
- "tj", "tk", "tm", "tn", "to", "tp", "tr", "tt", "tv", "tw", "tz", "ua", "ug", "uk", "um", "us", "uy", "uz", "va", "vc", "ve", "vg", "vi",
- "vn", "vu", "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw" };
-
/// <summary>
- /// This method checks the to see if a trust root represents a reasonable (sane) set of URLs.
+ /// Gets a value indicating whether this realm represents a reasonable (sane) set of URLs.
/// </summary>
/// <remarks>
/// 'http://*.com/', for example is not a reasonable pattern, as it cannot meaningfully
@@ -171,22 +159,26 @@ namespace DotNetOpenAuth.OpenId {
/// </remarks>
internal bool IsSane {
get {
- if (Host.Equals("localhost", StringComparison.OrdinalIgnoreCase))
+ if (this.Host.Equals("localhost", StringComparison.OrdinalIgnoreCase)) {
return true;
+ }
- string[] host_parts = Host.Split('.');
+ string[] host_parts = this.Host.Split('.');
string tld = host_parts[host_parts.Length - 1];
- if (Array.IndexOf(topLevelDomains, tld) < 0)
+ if (Array.IndexOf(topLevelDomains, tld) < 0) {
return false;
+ }
if (tld.Length == 2) {
- if (host_parts.Length == 1)
+ if (host_parts.Length == 1) {
return false;
+ }
- if (host_parts[host_parts.Length - 2].Length <= 3)
+ if (host_parts[host_parts.Length - 2].Length <= 3) {
return host_parts.Length > 2;
+ }
} else {
return host_parts.Length > 1;
}
@@ -196,12 +188,86 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
+ /// Implicitly converts the string-form of a URI to a <see cref="Realm"/> object.
+ /// </summary>
+ /// <param name="uri">The URI that the new Realm instance will represent.</param>
+ /// <returns>The result of the conversion.</returns>
+ [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "TODO")]
+ [SuppressMessage("Microsoft.Design", "CA1057:StringUriOverloadsCallSystemUriOverloads", Justification = "TODO")]
+ public static implicit operator Realm(string uri) {
+ return uri != null ? new Realm(uri) : null;
+ }
+
+ /// <summary>
+ /// Implicitly converts a <see cref="Uri"/> to a <see cref="Realm"/> object.
+ /// </summary>
+ /// <param name="uri">The URI to convert to a realm.</param>
+ /// <returns>The result of the conversion.</returns>
+ [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "TODO")]
+ public static implicit operator Realm(Uri uri) {
+ return uri != null ? new Realm(uri.AbsoluteUri) : null;
+ }
+
+ /// <summary>
+ /// Implicitly converts a <see cref="Realm"/> object to its <see cref="String"/> form.
+ /// </summary>
+ /// <param name="realm">The realm to convert to a string value.</param>
+ /// <returns>The result of the conversion.</returns>
+ public static implicit operator string(Realm realm) {
+ return realm != null ? realm.ToString() : null;
+ }
+
+ /// <summary>
+ /// Checks whether one <see cref="Realm"/> is equal to another.
+ /// </summary>
+ /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
+ /// <returns>
+ /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
+ /// </returns>
+ /// <exception cref="T:System.NullReferenceException">
+ /// The <paramref name="obj"/> parameter is null.
+ /// </exception>
+ public override bool Equals(object obj) {
+ Realm other = obj as Realm;
+ if (other == null) {
+ return false;
+ }
+ return this.uri.Equals(other.uri) && this.DomainWildcard == other.DomainWildcard;
+ }
+
+ /// <summary>
+ /// Returns the hash code used for storing this object in a hash table.
+ /// </summary>
+ /// <returns>
+ /// A hash code for the current <see cref="T:System.Object"/>.
+ /// </returns>
+ public override int GetHashCode() {
+ return this.uri.GetHashCode() + (this.DomainWildcard ? 1 : 0);
+ }
+
+ /// <summary>
+ /// Returns the string form of this <see cref="Realm"/>.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
+ /// </returns>
+ public override string ToString() {
+ if (this.DomainWildcard) {
+ UriBuilder builder = new UriBuilder(this.uri);
+ builder.Host = "*." + builder.Host;
+ return builder.ToStringWithImpliedPorts();
+ } else {
+ return this.uri.AbsoluteUri;
+ }
+ }
+
+ /// <summary>
/// Validates a URL against this trust root.
/// </summary>
/// <param name="url">A string specifying URL to check.</param>
/// <returns>Whether the given URL is within this trust root.</returns>
internal bool Contains(string url) {
- return Contains(new Uri(url));
+ return this.Contains(new Uri(url));
}
/// <summary>
@@ -210,26 +276,29 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="url">The URL to check.</param>
/// <returns>Whether the given URL is within this trust root.</returns>
internal bool Contains(Uri url) {
- if (url.Scheme != Scheme)
+ if (url.Scheme != this.Scheme) {
return false;
+ }
- if (url.Port != Port)
+ if (url.Port != this.Port) {
return false;
+ }
- if (!DomainWildcard) {
- if (url.Host != Host) {
+ if (!this.DomainWildcard) {
+ if (url.Host != this.Host) {
return false;
}
} else {
- Debug.Assert(!string.IsNullOrEmpty(Host), "The host part of the Regex should evaluate to at least one char for successful parsed trust roots.");
- string[] host_parts = Host.Split('.');
+ Debug.Assert(!string.IsNullOrEmpty(this.Host), "The host part of the Regex should evaluate to at least one char for successful parsed trust roots.");
+ string[] host_parts = this.Host.Split('.');
string[] url_parts = url.Host.Split('.');
// If the domain containing the wildcard has more parts than the URL to match against,
// it naturally can't be valid.
// Unless *.example.com actually matches example.com too.
- if (host_parts.Length > url_parts.Length)
+ if (host_parts.Length > url_parts.Length) {
return false;
+ }
// Compare last part first and move forward.
// Maybe could be done by using EndsWith, but piecewies helps ensure that
@@ -245,95 +314,89 @@ namespace DotNetOpenAuth.OpenId {
// If path matches or is specified to root ...
// (deliberately case sensitive to protect security on case sensitive systems)
- if (PathAndQuery.Equals(url.PathAndQuery, StringComparison.Ordinal)
- || PathAndQuery.Equals("/", StringComparison.Ordinal))
+ if (this.PathAndQuery.Equals(url.PathAndQuery, StringComparison.Ordinal)
+ || this.PathAndQuery.Equals("/", StringComparison.Ordinal)) {
return true;
+ }
// If trust root has a longer path, the return URL must be invalid.
- if (PathAndQuery.Length > url.PathAndQuery.Length)
+ if (this.PathAndQuery.Length > url.PathAndQuery.Length) {
return false;
+ }
// The following code assures that http://example.com/directory isn't below http://example.com/dir,
// but makes sure http://example.com/dir/ectory is below http://example.com/dir
- int path_len = PathAndQuery.Length;
+ int path_len = this.PathAndQuery.Length;
string url_prefix = url.PathAndQuery.Substring(0, path_len);
- if (PathAndQuery != url_prefix)
+ if (this.PathAndQuery != url_prefix) {
return false;
+ }
// If trust root includes a query string ...
- if (PathAndQuery.Contains("?")) {
+ if (this.PathAndQuery.Contains("?")) {
// ... make sure return URL begins with a new argument
return url.PathAndQuery[path_len] == '&';
}
// Or make sure a query string is introduced or a path below trust root
- return PathAndQuery.EndsWith("/", StringComparison.Ordinal)
+ return this.PathAndQuery.EndsWith("/", StringComparison.Ordinal)
|| url.PathAndQuery[path_len] == '?'
|| url.PathAndQuery[path_len] == '/';
}
#if DISCOVERY // TODO: Add discovery and then re-enable this code block
- /// <summary>
- /// Searches for an XRDS document at the realm URL, and if found, searches
- /// for a description of a relying party endpoints (OpenId login pages).
- /// </summary>
- /// <param name="allowRedirects">
- /// Whether redirects may be followed when discovering the Realm.
- /// This may be true when creating an unsolicited assertion, but must be
- /// false when performing return URL verification per 2.0 spec section 9.2.1.
- /// </param>
- /// <returns>The details of the endpoints if found, otherwise null.</returns>
- internal IEnumerable<DotNetOpenId.Provider.RelyingPartyReceivingEndpoint> Discover(bool allowRedirects) {
- // Attempt YADIS discovery
- DiscoveryResult yadisResult = Yadis.Yadis.Discover(UriWithWildcardChangedToWww, false);
- if (yadisResult != null) {
- if (!allowRedirects && yadisResult.NormalizedUri != yadisResult.RequestUri) {
- // Redirect occurred when it was not allowed.
- throw new OpenIdException(string.Format(CultureInfo.CurrentCulture,
- Strings.RealmCausedRedirectUponDiscovery, yadisResult.RequestUri));
- }
- if (yadisResult.IsXrds) {
- try {
- XrdsDocument xrds = new XrdsDocument(yadisResult.ResponseText);
- return xrds.FindRelyingPartyReceivingEndpoints();
- } catch (XmlException ex) {
- throw new OpenIdException(Strings.InvalidXRDSDocument, ex);
- }
- }
- }
- return new RelyingPartyReceivingEndpoint[0];
- }
+ /////// <summary>
+ /////// Searches for an XRDS document at the realm URL, and if found, searches
+ /////// for a description of a relying party endpoints (OpenId login pages).
+ /////// </summary>
+ /////// <param name="allowRedirects">
+ /////// Whether redirects may be followed when discovering the Realm.
+ /////// This may be true when creating an unsolicited assertion, but must be
+ /////// false when performing return URL verification per 2.0 spec section 9.2.1.
+ /////// </param>
+ /////// <returns>The details of the endpoints if found, otherwise null.</returns>
+ ////internal IEnumerable<DotNetOpenId.Provider.RelyingPartyReceivingEndpoint> Discover(bool allowRedirects) {
+ //// // Attempt YADIS discovery
+ //// DiscoveryResult yadisResult = Yadis.Yadis.Discover(UriWithWildcardChangedToWww, false);
+ //// if (yadisResult != null) {
+ //// if (!allowRedirects && yadisResult.NormalizedUri != yadisResult.RequestUri) {
+ //// // Redirect occurred when it was not allowed.
+ //// throw new OpenIdException(string.Format(CultureInfo.CurrentCulture,
+ //// Strings.RealmCausedRedirectUponDiscovery, yadisResult.RequestUri));
+ //// }
+ //// if (yadisResult.IsXrds) {
+ //// try {
+ //// XrdsDocument xrds = new XrdsDocument(yadisResult.ResponseText);
+ //// return xrds.FindRelyingPartyReceivingEndpoints();
+ //// } catch (XmlException ex) {
+ //// throw new OpenIdException(Strings.InvalidXRDSDocument, ex);
+ //// }
+ //// }
+ //// }
+ //// return new RelyingPartyReceivingEndpoint[0];
+ ////}
#endif
/// <summary>
- /// Checks whether one <see cref="Realm"/> is equal to another.
+ /// Calls <see cref="UriBuilder.ToString"/> if the argument is non-null.
+ /// Otherwise throws <see cref="ArgumentNullException"/>.
/// </summary>
- public override bool Equals(object obj) {
- Realm other = obj as Realm;
- if (other == null) return false;
- return uri.Equals(other.uri) && DomainWildcard == other.DomainWildcard;
- }
-
- /// <summary>
- /// Returns the hash code used for storing this object in a hash table.
- /// </summary>
- /// <returns></returns>
- public override int GetHashCode() {
- return uri.GetHashCode() + (DomainWildcard ? 1 : 0);
- }
-
- /// <summary>
- /// Returns the string form of this <see cref="Realm"/>.
- /// </summary>
- public override string ToString() {
- if (DomainWildcard) {
- UriBuilder builder = new UriBuilder(uri);
- builder.Host = "*." + builder.Host;
- return builder.ToStringWithImpliedPorts();
- } else {
- return uri.AbsoluteUri;
- }
+ /// <param name="realmUriBuilder">The realm URI builder.</param>
+ /// <returns>The result of UriBuilder.ToString()</returns>
+ /// <remarks>
+ /// This simple method is worthwhile because it checks for null
+ /// before dereferencing the UriBuilder. Since this is called from
+ /// within a constructor's base(...) call, this avoids a <see cref="NullReferenceException"/>
+ /// when we should be throwing an <see cref="ArgumentNullException"/>.
+ /// </remarks>
+ private static string SafeUriBuilderToString(UriBuilder realmUriBuilder) {
+ ErrorUtilities.VerifyArgumentNotNull(realmUriBuilder, "realmUriBuilder");
+
+ // Note: we MUST use ToString. Uri property throws if wildcard is present.
+ // TODO: I now know that Uri.ToString and Uri.AbsoluteUri are very different
+ // for some strings. Do we have to worry about that here?
+ return realmUriBuilder.ToString();
}
}
} \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/UriIdentifier.cs b/src/DotNetOpenAuth/OpenId/UriIdentifier.cs
index d4cba3a..865b895 100644
--- a/src/DotNetOpenAuth/OpenId/UriIdentifier.cs
+++ b/src/DotNetOpenAuth/OpenId/UriIdentifier.cs
@@ -1,8 +1,16 @@
-namespace DotNetOpenAuth.OpenId {
+//-----------------------------------------------------------------------
+// <copyright file="UriIdentifier.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId {
using System;
using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
using System.Text.RegularExpressions;
using System.Web.UI.HtmlControls;
+ using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.RelyingParty;
/// <summary>
@@ -10,84 +18,133 @@
/// </summary>
[Serializable]
public sealed class UriIdentifier : Identifier {
- static readonly string[] allowedSchemes = { "http", "https" };
/// <summary>
- /// Converts a <see cref="UriIdentifier"/> instance to a <see cref="Uri"/> instance.
+ /// The allowed protocol schemes in a URI Identifier.
/// </summary>
- public static implicit operator Uri(UriIdentifier identifier) {
- if (identifier == null) return null;
- return identifier.Uri;
- }
+ private static readonly string[] allowedSchemes = { "http", "https" };
+
/// <summary>
- /// Converts a <see cref="Uri"/> instance to a <see cref="UriIdentifier"/> instance.
+ /// Initializes a new instance of the <see cref="UriIdentifier"/> class.
/// </summary>
- public static implicit operator UriIdentifier(Uri identifier) {
- if (identifier == null) return null;
- return new UriIdentifier(identifier);
- }
-
+ /// <param name="uri">The value this identifier will represent.</param>
internal UriIdentifier(string uri) : this(uri, false) { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UriIdentifier"/> class.
+ /// </summary>
+ /// <param name="uri">The value this identifier will represent.</param>
+ /// <param name="requireSslDiscovery">if set to <c>true</c> [require SSL discovery].</param>
internal UriIdentifier(string uri, bool requireSslDiscovery)
: base(requireSslDiscovery) {
- if (string.IsNullOrEmpty(uri)) throw new ArgumentNullException("uri");
+ ErrorUtilities.VerifyNonZeroLength(uri, "uri");
Uri canonicalUri;
bool schemePrepended;
- if (!TryCanonicalize(uri, out canonicalUri, requireSslDiscovery, out schemePrepended))
+ if (!TryCanonicalize(uri, out canonicalUri, requireSslDiscovery, out schemePrepended)) {
throw new UriFormatException();
+ }
if (requireSslDiscovery && canonicalUri.Scheme != Uri.UriSchemeHttps) {
throw new ArgumentException(OpenIdStrings.ExplicitHttpUriSuppliedWithSslRequirement);
}
- Uri = canonicalUri;
- SchemeImplicitlyPrepended = schemePrepended;
+ this.Uri = canonicalUri;
+ this.SchemeImplicitlyPrepended = schemePrepended;
}
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UriIdentifier"/> class.
+ /// </summary>
+ /// <param name="uri">The value this identifier will represent.</param>
internal UriIdentifier(Uri uri) : this(uri, false) { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UriIdentifier"/> class.
+ /// </summary>
+ /// <param name="uri">The value this identifier will represent.</param>
+ /// <param name="requireSslDiscovery">if set to <c>true</c> [require SSL discovery].</param>
internal UriIdentifier(Uri uri, bool requireSslDiscovery)
: base(requireSslDiscovery) {
- if (uri == null) throw new ArgumentNullException("uri");
- if (!TryCanonicalize(new UriBuilder(uri), out uri))
+ ErrorUtilities.VerifyArgumentNotNull(uri, "uri");
+ if (!TryCanonicalize(new UriBuilder(uri), out uri)) {
throw new UriFormatException();
+ }
if (requireSslDiscovery && uri.Scheme != Uri.UriSchemeHttps) {
throw new ArgumentException(OpenIdStrings.ExplicitHttpUriSuppliedWithSslRequirement);
}
- Uri = uri;
- SchemeImplicitlyPrepended = false;
+ this.Uri = uri;
+ this.SchemeImplicitlyPrepended = false;
}
+ /// <summary>
+ /// Gets the URI this instance represents.
+ /// </summary>
internal Uri Uri { get; private set; }
+
/// <summary>
- /// Gets whether the scheme was missing when this Identifier was
- /// created and added automatically as part of the normalization
- /// process.
+ /// Gets a value indicating whether the scheme was missing when this
+ /// Identifier was created and added automatically as part of the
+ /// normalization process.
/// </summary>
internal bool SchemeImplicitlyPrepended { get; private set; }
- static bool isAllowedScheme(string uri) {
- if (string.IsNullOrEmpty(uri)) return false;
- return Array.FindIndex(allowedSchemes, s => uri.StartsWith(
- s + Uri.SchemeDelimiter, StringComparison.OrdinalIgnoreCase)) >= 0;
+ /// <summary>
+ /// Converts a <see cref="UriIdentifier"/> instance to a <see cref="Uri"/> instance.
+ /// </summary>
+ /// <param name="identifier">The identifier to convert to an ordinary <see cref="Uri"/> instance.</param>
+ /// <returns>The result of the conversion.</returns>
+ public static implicit operator Uri(UriIdentifier identifier) {
+ if (identifier == null) {
+ return null;
+ }
+ return identifier.Uri;
}
- static bool isAllowedScheme(Uri uri) {
- if (uri == null) return false;
- return Array.FindIndex(allowedSchemes, s =>
- uri.Scheme.Equals(s, StringComparison.OrdinalIgnoreCase)) >= 0;
+
+ /// <summary>
+ /// Converts a <see cref="Uri"/> instance to a <see cref="UriIdentifier"/> instance.
+ /// </summary>
+ /// <param name="identifier">The <see cref="Uri"/> instance to turn into a <see cref="UriIdentifier"/>.</param>
+ /// <returns>The result of the conversion.</returns>
+ public static implicit operator UriIdentifier(Uri identifier) {
+ if (identifier == null) {
+ return null;
+ }
+ return new UriIdentifier(identifier);
}
- static bool TryCanonicalize(string uri, out Uri canonicalUri, bool forceHttpsDefaultScheme, out bool schemePrepended) {
- canonicalUri = null;
- schemePrepended = false;
- try {
- // Assume http:// scheme if an allowed scheme isn't given, and strip
- // fragments off. Consistent with spec section 7.2#3
- if (!isAllowedScheme(uri)) {
- uri = (forceHttpsDefaultScheme ? Uri.UriSchemeHttps : Uri.UriSchemeHttp) +
- Uri.SchemeDelimiter + uri;
- schemePrepended = true;
- }
- // Use a UriBuilder because it helps to normalize the URL as well.
- return TryCanonicalize(new UriBuilder(uri), out canonicalUri);
- } catch (UriFormatException) {
- // We try not to land here with checks in the try block, but just in case.
+
+ /// <summary>
+ /// Tests equality between this URI and another URI.
+ /// </summary>
+ /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
+ /// <returns>
+ /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
+ /// </returns>
+ /// <exception cref="T:System.NullReferenceException">
+ /// The <paramref name="obj"/> parameter is null.
+ /// </exception>
+ public override bool Equals(object obj) {
+ UriIdentifier other = obj as UriIdentifier;
+ if (other == null) {
return false;
}
+ return this.Uri == other.Uri;
+ }
+
+ /// <summary>
+ /// Returns the hash code of this XRI.
+ /// </summary>
+ /// <returns>
+ /// A hash code for the current <see cref="T:System.Object"/>.
+ /// </returns>
+ public override int GetHashCode() {
+ return Uri.GetHashCode();
+ }
+
+ /// <summary>
+ /// Returns the string form of the URI.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
+ /// </returns>
+ public override string ToString() {
+ return Uri.AbsoluteUri;
}
#if UNUSED
static bool TryCanonicalize(string uri, out string canonicalUri) {
@@ -98,29 +155,41 @@
}
#endif
/// <summary>
- /// Removes the fragment from a URL and sets the host to lowercase.
+ /// Determines whether a URI is a valid OpenID Identifier (of any kind).
/// </summary>
+ /// <param name="uri">The URI to test for OpenID validity.</param>
+ /// <returns>
+ /// <c>true</c> if the identifier is valid; otherwise, <c>false</c>.
+ /// </returns>
/// <remarks>
- /// This does NOT standardize an OpenID URL for storage in a database, as
- /// it does nothing to convert the URL to a Claimed Identifier, besides the fact
- /// that it only deals with URLs whereas OpenID 2.0 supports XRIs.
- /// For this, you should lookup the value stored in IAuthenticationResponse.ClaimedIdentifier.
+ /// A valid URI is absolute (not relative) and uses an http(s) scheme.
/// </remarks>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
- static bool TryCanonicalize(UriBuilder uriBuilder, out Uri canonicalUri) {
- uriBuilder.Host = uriBuilder.Host.ToLowerInvariant();
- canonicalUri = uriBuilder.Uri;
- return true;
- }
internal static bool IsValidUri(string uri) {
Uri normalized;
bool schemePrepended;
return TryCanonicalize(uri, out normalized, false, out schemePrepended);
}
+
+ /// <summary>
+ /// Determines whether a URI is a valid OpenID Identifier (of any kind).
+ /// </summary>
+ /// <param name="uri">The URI to test for OpenID validity.</param>
+ /// <returns>
+ /// <c>true</c> if the identifier is valid; otherwise, <c>false</c>.
+ /// </returns>
+ /// <remarks>
+ /// A valid URI is absolute (not relative) and uses an http(s) scheme.
+ /// </remarks>
internal static bool IsValidUri(Uri uri) {
- if (uri == null) return false;
- if (!uri.IsAbsoluteUri) return false;
- if (!isAllowedScheme(uri)) return false;
+ if (uri == null) {
+ return false;
+ }
+ if (!uri.IsAbsoluteUri) {
+ return false;
+ }
+ if (!IsAllowedScheme(uri)) {
+ return false;
+ }
return true;
}
@@ -217,10 +286,20 @@
}
#endif
+ /// <summary>
+ /// Returns an <see cref="Identifier"/> that has no URI fragment.
+ /// Quietly returns the original <see cref="Identifier"/> if it is not
+ /// a <see cref="UriIdentifier"/> or no fragment exists.
+ /// </summary>
+ /// <returns>
+ /// A new <see cref="Identifier"/> instance if there was a
+ /// fragment to remove, otherwise this same instance..
+ /// </returns>
internal override Identifier TrimFragment() {
// If there is no fragment, we have no need to rebuild the Identifier.
- if (Uri.Fragment == null || Uri.Fragment.Length == 0)
+ if (Uri.Fragment == null || Uri.Fragment.Length == 0) {
return this;
+ }
// Strip the fragment.
UriBuilder builder = new UriBuilder(Uri);
@@ -228,6 +307,18 @@
return builder.Uri;
}
+ /// <summary>
+ /// Converts a given identifier to its secure equivalent.
+ /// UriIdentifiers originally created with an implied HTTP scheme change to HTTPS.
+ /// Discovery is made to require SSL for the entire resolution process.
+ /// </summary>
+ /// <param name="secureIdentifier">The newly created secure identifier.
+ /// If the conversion fails, <paramref name="secureIdentifier"/> retains
+ /// <i>this</i> identifiers identity, but will never discover any endpoints.</param>
+ /// <returns>
+ /// True if the secure conversion was successful.
+ /// False if the Identifier was originally created with an explicit HTTP scheme.
+ /// </returns>
internal override bool TryRequireSsl(out Identifier secureIdentifier) {
// If this Identifier is already secure, reuse it.
if (IsDiscoverySecureEndToEnd) {
@@ -243,7 +334,7 @@
}
// Otherwise, try to make this Identifier secure by normalizing to HTTPS instead of HTTP.
- if (SchemeImplicitlyPrepended) {
+ if (this.SchemeImplicitlyPrepended) {
UriBuilder newIdentifierUri = new UriBuilder(this.Uri);
newIdentifierUri.Scheme = Uri.UriSchemeHttps;
if (newIdentifierUri.Port == 80) {
@@ -259,26 +350,95 @@
}
/// <summary>
- /// Tests equality between this URI and another URI.
+ /// Determines whether the given URI is using a scheme in the list of allowed schemes.
/// </summary>
- public override bool Equals(object obj) {
- UriIdentifier other = obj as UriIdentifier;
- if (other == null) return false;
- return this.Uri == other.Uri;
+ /// <param name="uri">The URI whose scheme is to be checked.</param>
+ /// <returns>
+ /// <c>true</c> if the scheme is allowed; otherwise, <c>false</c>.
+ /// <c>false</c> is also returned if <paramref name="uri"/> is null.
+ /// </returns>
+ private static bool IsAllowedScheme(string uri) {
+ if (string.IsNullOrEmpty(uri)) {
+ return false;
+ }
+ return Array.FindIndex(
+ allowedSchemes,
+ s => uri.StartsWith(s + Uri.SchemeDelimiter, StringComparison.OrdinalIgnoreCase)) >= 0;
}
/// <summary>
- /// Returns the hash code of this XRI.
+ /// Determines whether the given URI is using a scheme in the list of allowed schemes.
/// </summary>
- public override int GetHashCode() {
- return Uri.GetHashCode();
+ /// <param name="uri">The URI whose scheme is to be checked.</param>
+ /// <returns>
+ /// <c>true</c> if the scheme is allowed; otherwise, <c>false</c>.
+ /// <c>false</c> is also returned if <paramref name="uri"/> is null.
+ /// </returns>
+ private static bool IsAllowedScheme(Uri uri) {
+ if (uri == null) {
+ return false;
+ }
+ return Array.FindIndex(
+ allowedSchemes,
+ s => uri.Scheme.Equals(s, StringComparison.OrdinalIgnoreCase)) >= 0;
}
/// <summary>
- /// Returns the string form of the URI.
+ /// Tries to canonicalize a user-supplied identifier.
+ /// This does NOT convert a user-supplied identifier to a Claimed Identifier!
/// </summary>
- public override string ToString() {
- return Uri.AbsoluteUri;
+ /// <param name="uri">The user-supplied identifier.</param>
+ /// <param name="canonicalUri">The resulting canonical URI.</param>
+ /// <param name="forceHttpsDefaultScheme">If set to <c>true</c> and the user-supplied identifier lacks a scheme, the "https://" scheme will be prepended instead of the standard "http://" one.</param>
+ /// <param name="schemePrepended">if set to <c>true</c> [scheme prepended].</param>
+ /// <returns>
+ /// <c>true</c> if the identifier was valid and could be canonicalized.
+ /// <c>false</c> if the identifier is outside the scope of allowed inputs and should be rejected.
+ /// </returns>
+ /// <remarks>
+ /// Canonicalization is done by adding a scheme in front of an
+ /// identifier if it isn't already present. Other trivial changes that do not
+ /// require network access are also done, such as lower-casing the hostname in the URI.
+ /// </remarks>
+ private static bool TryCanonicalize(string uri, out Uri canonicalUri, bool forceHttpsDefaultScheme, out bool schemePrepended) {
+ ErrorUtilities.VerifyNonZeroLength(uri, "uri");
+
+ canonicalUri = null;
+ schemePrepended = false;
+ try {
+ // Assume http:// scheme if an allowed scheme isn't given, and strip
+ // fragments off. Consistent with spec section 7.2#3
+ if (!IsAllowedScheme(uri)) {
+ uri = (forceHttpsDefaultScheme ? Uri.UriSchemeHttps : Uri.UriSchemeHttp) +
+ Uri.SchemeDelimiter + uri;
+ schemePrepended = true;
+ }
+
+ // Use a UriBuilder because it helps to normalize the URL as well.
+ return TryCanonicalize(new UriBuilder(uri), out canonicalUri);
+ } catch (UriFormatException) {
+ // We try not to land here with checks in the try block, but just in case.
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Removes the fragment from a URL and sets the host to lowercase.
+ /// </summary>
+ /// <param name="uriBuilder">The URI builder with the value to canonicalize.</param>
+ /// <param name="canonicalUri">The resulting canonical URI.</param>
+ /// <returns><c>true</c> if the canonicalization was successful; <c>false</c> otherwise.</returns>
+ /// <remarks>
+ /// This does NOT standardize an OpenID URL for storage in a database, as
+ /// it does nothing to convert the URL to a Claimed Identifier, besides the fact
+ /// that it only deals with URLs whereas OpenID 2.0 supports XRIs.
+ /// For this, you should lookup the value stored in IAuthenticationResponse.ClaimedIdentifier.
+ /// </remarks>
+ [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "The user will see the result of this operation and they want to see it in lower case.")]
+ private static bool TryCanonicalize(UriBuilder uriBuilder, out Uri canonicalUri) {
+ uriBuilder.Host = uriBuilder.Host.ToLowerInvariant();
+ canonicalUri = uriBuilder.Uri;
+ return true;
}
}
}
diff --git a/src/DotNetOpenAuth/OpenId/XriIdentifier.cs b/src/DotNetOpenAuth/OpenId/XriIdentifier.cs
index 663bb49..4e92eff 100644
--- a/src/DotNetOpenAuth/OpenId/XriIdentifier.cs
+++ b/src/DotNetOpenAuth/OpenId/XriIdentifier.cs
@@ -1,8 +1,15 @@
-namespace DotNetOpenAuth.OpenId {
+//-----------------------------------------------------------------------
+// <copyright file="XriIdentifier.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId {
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Xml;
+ using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.RelyingParty;
/// <summary>
@@ -10,130 +17,203 @@
/// </summary>
[Serializable]
public sealed class XriIdentifier : Identifier {
+ /// <summary>
+ /// An XRI always starts with one of these symbols.
+ /// </summary>
internal static readonly char[] GlobalContextSymbols = { '=', '@', '+', '$', '!' };
- const string xriScheme = "xri://";
+ /// <summary>
+ /// The scheme and separator "xri://"
+ /// </summary>
+ private const string XriScheme = "xri://";
+
+ /// <summary>
+ /// The magic URL that will provide us an XRDS document for a given XRI identifier.
+ /// </summary>
+ /// <remarks>
+ /// We use application/xrd+xml instead of application/xrds+xml because it gets
+ /// xri.net to automatically give us exactly the right XRD element for community i-names
+ /// automatically, saving us having to choose which one to use out of the result.
+ /// The ssl=true parameter tells the proxy resolver to accept only SSL connections
+ /// when resolving community i-names.
+ /// </remarks>
+ private const string XriResolverProxyTemplate = "https://xri.net/{0}?_xrd_r=application/xrd%2Bxml;sep=false";
+
+ /// <summary>
+ /// The XRI proxy resolver to use for finding XRDS documents from an XRI.
+ /// </summary>
+ private readonly string xriResolverProxy;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="XriIdentifier"/> class.
+ /// </summary>
+ /// <param name="xri">The string value of the XRI.</param>
internal XriIdentifier(string xri) : this(xri, false) { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="XriIdentifier"/> class.
+ /// </summary>
+ /// <param name="xri">The XRI that this Identifier will represent.</param>
+ /// <param name="requireSsl">
+ /// If set to <c>true</c>, discovery and the initial authentication redirect will
+ /// only succeed if it can be done entirely using SSL.
+ /// </param>
internal XriIdentifier(string xri, bool requireSsl)
: base(requireSsl) {
- if (!IsValidXri(xri))
- throw new FormatException(string.Format(CultureInfo.CurrentCulture,
- OpenIdStrings.InvalidXri, xri));
- xriResolverProxy = xriResolverProxyTemplate;
+ if (!IsValidXri(xri)) {
+ throw new FormatException(
+ string.Format(CultureInfo.CurrentCulture, OpenIdStrings.InvalidXri, xri));
+ }
+ this.xriResolverProxy = XriResolverProxyTemplate;
if (requireSsl) {
// Indicate to xri.net that we require SSL to be used for delegated resolution
// of community i-names.
- xriResolverProxy += ";https=true";
+ this.xriResolverProxy += ";https=true";
}
- OriginalXri = xri;
- CanonicalXri = canonicalizeXri(xri);
+ this.OriginalXri = xri;
+ this.CanonicalXri = CanonicalizeXri(xri);
}
/// <summary>
- /// The original XRI supplied to the constructor.
+ /// Gets the original XRI supplied to the constructor.
/// </summary>
internal string OriginalXri { get; private set; }
+
/// <summary>
- /// The canonical form of the XRI string.
+ /// Gets the canonical form of the XRI string.
/// </summary>
internal string CanonicalXri { get; private set; }
/// <summary>
- /// Tests whether a given string represents a valid XRI format.
+ /// Gets the URL from which this XRI's XRDS document may be downloaded.
/// </summary>
- internal static bool IsValidXri(string xri) {
- if (string.IsNullOrEmpty(xri)) throw new ArgumentNullException("xri");
- // TODO: better validation code here
- return xri.IndexOfAny(GlobalContextSymbols) == 0
- || xri.StartsWith("(", StringComparison.Ordinal)
- || xri.StartsWith(xriScheme, StringComparison.OrdinalIgnoreCase);
+ private Uri XrdsUrl {
+ get { return new Uri(string.Format(CultureInfo.InvariantCulture, this.xriResolverProxy, this)); }
}
/// <summary>
- /// Takes any valid form of XRI string and returns the canonical form of the same XRI.
+ /// Tests equality between this XRI and another XRI.
/// </summary>
- static string canonicalizeXri(string xri) {
- xri = xri.Trim();
- if (xri.StartsWith(xriScheme, StringComparison.OrdinalIgnoreCase))
- xri = xri.Substring(xriScheme.Length);
- return xri;
+ /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
+ /// <returns>
+ /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
+ /// </returns>
+ /// <exception cref="T:System.NullReferenceException">
+ /// The <paramref name="obj"/> parameter is null.
+ /// </exception>
+ public override bool Equals(object obj) {
+ XriIdentifier other = obj as XriIdentifier;
+ if (other == null) {
+ return false;
+ }
+ return this.CanonicalXri == other.CanonicalXri;
}
/// <summary>
- /// The magic URL that will provide us an XRDS document for a given XRI identifier.
- /// </summary>
- /// <remarks>
- /// We use application/xrd+xml instead of application/xrds+xml because it gets
- /// xri.net to automatically give us exactly the right XRD element for community i-names
- /// automatically, saving us having to choose which one to use out of the result.
- /// The ssl=true parameter tells the proxy resolver to accept only SSL connections
- /// when resolving community i-names.
- /// </remarks>
- const string xriResolverProxyTemplate = "https://xri.net/{0}?_xrd_r=application/xrd%2Bxml;sep=false";
- readonly string xriResolverProxy;
- /// <summary>
- /// Resolves the XRI to a URL from which an XRDS document may be downloaded.
+ /// Returns the hash code of this XRI.
/// </summary>
- private Uri XrdsUrl {
- get {
- return new Uri(string.Format(CultureInfo.InvariantCulture,
- xriResolverProxy, this));
- }
- }
-
-#if DISCOVERY // TODO: Add discovery and then re-enable this code block
- XrdsDocument downloadXrds() {
- var xrdsResponse = UntrustedWebRequest.Request(XrdsUrl);
- XrdsDocument doc = new XrdsDocument(XmlReader.Create(xrdsResponse.ResponseStream));
- if (!doc.IsXrdResolutionSuccessful) {
- throw new OpenIdException(Strings.XriResolutionFailed);
- }
- return doc;
+ /// <returns>
+ /// A hash code for the current <see cref="T:System.Object"/>.
+ /// </returns>
+ public override int GetHashCode() {
+ return this.CanonicalXri.GetHashCode();
}
- internal override IEnumerable<ServiceEndpoint> Discover() {
- return downloadXrds().CreateServiceEndpoints(this);
+ /// <summary>
+ /// Returns the canonical string form of the XRI.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
+ /// </returns>
+ public override string ToString() {
+ return this.CanonicalXri;
}
/// <summary>
- /// Performs discovery on THIS identifier, but generates <see cref="ServiceEndpoint"/>
- /// instances that treat another given identifier as the user-supplied identifier.
+ /// Tests whether a given string represents a valid XRI format.
/// </summary>
- internal IEnumerable<ServiceEndpoint> Discover(XriIdentifier userSuppliedIdentifier) {
- return downloadXrds().CreateServiceEndpoints(userSuppliedIdentifier);
- }
-#endif
+ /// <param name="xri">The value to test for XRI validity.</param>
+ /// <returns>
+ /// <c>true</c> if the given string constitutes a valid XRI; otherwise, <c>false</c>.
+ /// </returns>
+ internal static bool IsValidXri(string xri) {
+ ErrorUtilities.VerifyNonZeroLength(xri, "xri");
- internal override Identifier TrimFragment() {
- return this;
+ // TODO: better validation code here
+ return xri.IndexOfAny(GlobalContextSymbols) == 0
+ || xri.StartsWith("(", StringComparison.Ordinal)
+ || xri.StartsWith(XriScheme, StringComparison.OrdinalIgnoreCase);
}
- internal override bool TryRequireSsl(out Identifier secureIdentifier) {
- secureIdentifier = IsDiscoverySecureEndToEnd ? this : new XriIdentifier(this, true);
- return true;
- }
+#if DISCOVERY // TODO: Add discovery and then re-enable this code block
+ ////private XrdsDocument downloadXrds() {
+ //// var xrdsResponse = UntrustedWebRequest.Request(XrdsUrl);
+ //// XrdsDocument doc = new XrdsDocument(XmlReader.Create(xrdsResponse.ResponseStream));
+ //// if (!doc.IsXrdResolutionSuccessful) {
+ //// throw new OpenIdException(Strings.XriResolutionFailed);
+ //// }
+ //// return doc;
+ ////}
+
+ ////internal override IEnumerable<ServiceEndpoint> Discover() {
+ //// return downloadXrds().CreateServiceEndpoints(this);
+ ////}
+
+ /////// <summary>
+ /////// Performs discovery on THIS identifier, but generates <see cref="ServiceEndpoint"/>
+ /////// instances that treat another given identifier as the user-supplied identifier.
+ /////// </summary>
+ ////internal IEnumerable<ServiceEndpoint> Discover(XriIdentifier userSuppliedIdentifier) {
+ //// return downloadXrds().CreateServiceEndpoints(userSuppliedIdentifier);
+ ////}
+#endif
/// <summary>
- /// Tests equality between this XRI and another XRI.
+ /// Returns an <see cref="Identifier"/> that has no URI fragment.
+ /// Quietly returns the original <see cref="Identifier"/> if it is not
+ /// a <see cref="UriIdentifier"/> or no fragment exists.
/// </summary>
- public override bool Equals(object obj) {
- XriIdentifier other = obj as XriIdentifier;
- if (other == null) return false;
- return this.CanonicalXri == other.CanonicalXri;
+ /// <returns>
+ /// A new <see cref="Identifier"/> instance if there was a
+ /// fragment to remove, otherwise this same instance..
+ /// </returns>
+ /// <remarks>
+ /// XRI Identifiers never have a fragment part, and thus this method
+ /// always returns this same instance.
+ /// </remarks>
+ internal override Identifier TrimFragment() {
+ return this;
}
/// <summary>
- /// Returns the hash code of this XRI.
+ /// Converts a given identifier to its secure equivalent.
+ /// UriIdentifiers originally created with an implied HTTP scheme change to HTTPS.
+ /// Discovery is made to require SSL for the entire resolution process.
/// </summary>
- public override int GetHashCode() {
- return CanonicalXri.GetHashCode();
+ /// <param name="secureIdentifier">The newly created secure identifier.
+ /// If the conversion fails, <paramref name="secureIdentifier"/> retains
+ /// <i>this</i> identifiers identity, but will never discover any endpoints.</param>
+ /// <returns>
+ /// True if the secure conversion was successful.
+ /// False if the Identifier was originally created with an explicit HTTP scheme.
+ /// </returns>
+ internal override bool TryRequireSsl(out Identifier secureIdentifier) {
+ secureIdentifier = IsDiscoverySecureEndToEnd ? this : new XriIdentifier(this, true);
+ return true;
}
/// <summary>
- /// Returns the canonical string form of the XRI.
+ /// Takes any valid form of XRI string and returns the canonical form of the same XRI.
/// </summary>
- public override string ToString() {
- return CanonicalXri;
+ /// <param name="xri">The xri to canonicalize.</param>
+ /// <returns>The canonicalized form of the XRI.</returns>
+ /// <remarks>The canonical form, per the OpenID spec, is no scheme and no whitespace on either end.</remarks>
+ private static string CanonicalizeXri(string xri) {
+ xri = xri.Trim();
+ if (xri.StartsWith(XriScheme, StringComparison.OrdinalIgnoreCase)) {
+ xri = xri.Substring(XriScheme.Length);
+ }
+ return xri;
}
}
}
diff --git a/src/DotNetOpenAuth/UriUtil.cs b/src/DotNetOpenAuth/UriUtil.cs
index 3385e4e..a5d0df9 100644
--- a/src/DotNetOpenAuth/UriUtil.cs
+++ b/src/DotNetOpenAuth/UriUtil.cs
@@ -8,9 +8,9 @@ namespace DotNetOpenAuth {
using System;
using System.Collections.Specialized;
using System.Linq;
+ using System.Text.RegularExpressions;
using System.Web;
using DotNetOpenAuth.Messaging;
- using System.Text.RegularExpressions;
/// <summary>
/// Utility methods for working with URIs.
@@ -49,7 +49,9 @@ namespace DotNetOpenAuth {
/// Equivalent to UriBuilder.ToString() but omits port # if it may be implied.
/// Equivalent to UriBuilder.Uri.ToString(), but doesn't throw an exception if the Host has a wildcard.
/// </summary>
- public static string ToStringWithImpliedPorts(this UriBuilder builder) {
+ /// <param name="builder">The UriBuilder to render as a string.</param>
+ /// <returns>The string version of the Uri.</returns>
+ internal static string ToStringWithImpliedPorts(this UriBuilder builder) {
ErrorUtilities.VerifyArgumentNotNull(builder, "builder");
// We only check for implied ports on HTTP and HTTPS schemes since those
@@ -58,6 +60,7 @@ namespace DotNetOpenAuth {
(builder.Port == 443 && string.Equals(builder.Scheme, "https", StringComparison.OrdinalIgnoreCase))) {
// An implied port may be removed.
string url = builder.ToString();
+
// Be really careful to only remove the first :80 or :443 so we are guaranteed
// we're removing only the port (and not something in the query string that
// looks like a port.
diff --git a/src/DotNetOpenAuth/Util.cs b/src/DotNetOpenAuth/Util.cs
index a4fdb9d..a043c7a 100644
--- a/src/DotNetOpenAuth/Util.cs
+++ b/src/DotNetOpenAuth/Util.cs
@@ -27,5 +27,27 @@ namespace DotNetOpenAuth {
return string.Format(CultureInfo.InvariantCulture, "{0} ({1})", assemblyFullName, official ? "official" : "private");
}
}
+
+ /// <summary>
+ /// Tests for equality between two objects. Safely handles the case where one or both are null.
+ /// </summary>
+ /// <typeparam name="T">The type of objects been checked for equality.</typeparam>
+ /// <param name="first">The first object.</param>
+ /// <param name="second">The second object.</param>
+ /// <returns><c>true</c> if the two objects are equal; <c>false</c> otherwise.</returns>
+ internal static bool EqualsNullSafe<T>(this T first, T second) where T : class {
+ // If one is null and the other is not...
+ if (object.ReferenceEquals(first, null) ^ object.ReferenceEquals(second, null)) {
+ return false;
+ }
+
+ // If both are null... (we only check one because we already know both are either null or non-null)
+ if (object.ReferenceEquals(first, null)) {
+ return true;
+ }
+
+ // Neither are null. Delegate to the Equals method.
+ return first.Equals(second);
+ }
}
}