summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2010-06-08 22:38:46 -0700
committerAndrew Arnott <andrewarnott@gmail.com>2010-06-08 22:38:46 -0700
commitc604f25eaf5cd4fce2a73e00bfe8b27fe97b86d0 (patch)
treee37f874cfe3a0937059e4acf1090254ce0b3dba6 /src
parentb3a35b9ae0823dda35bb07b404c9c09fec09402a (diff)
parent436da522dcab28df4e19b212b49270c5211ff92b (diff)
downloadDotNetOpenAuth-c604f25eaf5cd4fce2a73e00bfe8b27fe97b86d0.zip
DotNetOpenAuth-c604f25eaf5cd4fce2a73e00bfe8b27fe97b86d0.tar.gz
DotNetOpenAuth-c604f25eaf5cd4fce2a73e00bfe8b27fe97b86d0.tar.bz2
Merge branch 'v3.4' into oauth2
Conflicts: src/DotNetOpenAuth/Configuration/MessagingElement.cs src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj14
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/Reflection/ValueMappingTests.cs4
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Messages/CheckAuthenticationRequestTests.cs31
-rw-r--r--src/DotNetOpenAuth/Configuration/MessagingElement.cs23
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj4
-rw-r--r--src/DotNetOpenAuth/Messaging/Channel.cs2
-rw-r--r--src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs8
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingUtilities.cs2
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/IMessagePartOriginalEncoder.cs22
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs15
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs14
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs25
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs63
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs21
-rw-r--r--src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs4
-rw-r--r--src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs4
-rw-r--r--src/DotNetOpenAuth/Mvc/OpenIdAjaxOptions.cs17
-rw-r--r--src/DotNetOpenAuth/Mvc/OpenIdHelper.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs1
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationResponse.cs6
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs6
-rw-r--r--src/DotNetOpenAuth/OpenId/Realm.cs8
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.js10
-rw-r--r--src/DotNetOpenAuth/OpenId/UriIdentifier.cs15
25 files changed, 256 insertions, 67 deletions
diff --git a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj
index fbb7779..26d870b 100644
--- a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj
+++ b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj
@@ -138,18 +138,10 @@
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
- <Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\..\lib\log4net.dll</HintPath>
- </Reference>
+ <Reference Include="log4net" />
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" />
- <Reference Include="Moq, Version=3.1.416.3, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\..\lib\Moq.dll</HintPath>
- </Reference>
- <Reference Include="nunit.framework">
- <HintPath>..\..\lib\nunit.framework.dll</HintPath>
- </Reference>
+ <Reference Include="Moq" />
+ <Reference Include="NUnit.Framework"/>
<Reference Include="System" />
<Reference Include="System.configuration" />
<Reference Include="System.Core">
diff --git a/src/DotNetOpenAuth.Test/Messaging/Reflection/ValueMappingTests.cs b/src/DotNetOpenAuth.Test/Messaging/Reflection/ValueMappingTests.cs
index d556b11..60c8bc3 100644
--- a/src/DotNetOpenAuth.Test/Messaging/Reflection/ValueMappingTests.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/Reflection/ValueMappingTests.cs
@@ -13,12 +13,12 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection {
public class ValueMappingTests {
[TestCase, ExpectedException(typeof(ArgumentNullException))]
public void CtorNullToString() {
- new ValueMapping(null, str => new object());
+ new ValueMapping(null, null, str => new object());
}
[TestCase, ExpectedException(typeof(ArgumentNullException))]
public void CtorNullToObject() {
- new ValueMapping(obj => obj.ToString(), null);
+ new ValueMapping(obj => obj.ToString(), null, null);
}
}
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/Messages/CheckAuthenticationRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Messages/CheckAuthenticationRequestTests.cs
index cf6b814..d2d2cc4 100644
--- a/src/DotNetOpenAuth.Test/OpenId/Messages/CheckAuthenticationRequestTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/Messages/CheckAuthenticationRequestTests.cs
@@ -6,12 +6,37 @@
namespace DotNetOpenAuth.Test.OpenId.Messages {
using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.Messages;
using NUnit.Framework;
[TestFixture]
public class CheckAuthenticationRequestTests : OpenIdTestBase {
+ /// <summary>
+ /// Verifies that the check_auth request is sent preserving EXACTLY (non-normalized)
+ /// what is in the positive assertion.
+ /// </summary>
+ /// <remarks>
+ /// This is very important because any normalization
+ /// (like changing https://host:443/ to https://host/) in the message will invalidate the signature
+ /// and cause the authentication to inappropriately fail.
+ /// Designed to verify fix to Trac #198.
+ /// </remarks>
+ [TestCase]
+ public void ExactPositiveAssertionPreservation() {
+ var rp = CreateRelyingParty(true);
+
+ // Initialize the positive assertion response with some data that is NOT in normalized form.
+ var positiveAssertion = new PositiveAssertionResponse(Protocol.Default.Version, RPUri)
+ {
+ ClaimedIdentifier = "https://HOST:443/a",
+ ProviderEndpoint = new Uri("https://anotherHOST:443/b"),
+ };
+
+ var checkAuth = new CheckAuthenticationRequest(positiveAssertion, rp.Channel);
+ var actual = rp.Channel.MessageDescriptions.GetAccessor(checkAuth);
+ Assert.AreEqual("https://HOST:443/a", actual["openid.claimed_id"]);
+ Assert.AreEqual("https://anotherHOST:443/b", actual["openid.op_endpoint"]);
+ }
}
}
diff --git a/src/DotNetOpenAuth/Configuration/MessagingElement.cs b/src/DotNetOpenAuth/Configuration/MessagingElement.cs
index cdb4dc2..fae18ec 100644
--- a/src/DotNetOpenAuth/Configuration/MessagingElement.cs
+++ b/src/DotNetOpenAuth/Configuration/MessagingElement.cs
@@ -37,6 +37,11 @@ namespace DotNetOpenAuth.Configuration {
private const string RelaxSslRequirementsConfigName = "relaxSslRequirements";
/// <summary>
+ /// The name of the attribute that controls whether messaging rules are strictly followed.
+ /// </summary>
+ private const string StrictConfigName = "strict";
+
+ /// <summary>
/// Gets the actual maximum message lifetime that a program should allow.
/// </summary>
/// <value>The sum of the <see cref="MaximumMessageLifetime"/> and
@@ -98,6 +103,24 @@ namespace DotNetOpenAuth.Configuration {
}
/// <summary>
+ /// Gets or sets a value indicating whether messaging rules are strictly
+ /// adhered to.
+ /// </summary>
+ /// <value><c>true</c> by default.</value>
+ /// <remarks>
+ /// Strict will require that remote parties adhere strictly to the specifications,
+ /// even when a loose interpretation would not compromise security.
+ /// <c>true</c> is a good default because it shakes out interoperability bugs in remote services
+ /// so they can be identified and corrected. But some web sites want things to Just Work
+ /// more than they want to file bugs against others, so <c>false</c> is the setting for them.
+ /// </remarks>
+ [ConfigurationProperty(StrictConfigName, DefaultValue = true)]
+ internal bool Strict {
+ get { return (bool)this[StrictConfigName]; }
+ set { this[StrictConfigName] = value; }
+ }
+
+ /// <summary>
/// Gets or sets the configuration for the <see cref="UntrustedWebRequestHandler"/> class.
/// </summary>
/// <value>The untrusted web request.</value>
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
index 650e01a..3626101 100644
--- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj
+++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
@@ -168,7 +168,6 @@ http://opensource.org/licenses/ms-pl.html
<ItemGroup>
<Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
- <HintPath>..\..\lib\log4net.dll</HintPath>
</Reference>
<Reference Include="PresentationFramework">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
@@ -198,7 +197,6 @@ http://opensource.org/licenses/ms-pl.html
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Web.Abstractions">
- <HintPath>..\..\lib\System.Web.Abstractions.dll</HintPath>
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Web.Extensions">
@@ -209,7 +207,6 @@ http://opensource.org/licenses/ms-pl.html
</Reference>
<Reference Include="System.Web.Mobile" Condition=" '$(ClrVersion)' != '4' " />
<Reference Include="System.Web.Routing">
- <HintPath>..\..\lib\System.Web.Routing.dll</HintPath>
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Xaml" Condition=" '$(ClrVersion)' == '4' " />
@@ -304,6 +301,7 @@ http://opensource.org/licenses/ms-pl.html
<Compile Include="Messaging\OutgoingWebResponseActionResult.cs" />
<Compile Include="Messaging\Reflection\IMessagePartEncoder.cs" />
<Compile Include="Messaging\Reflection\IMessagePartNullEncoder.cs" />
+ <Compile Include="Messaging\Reflection\IMessagePartOriginalEncoder.cs" />
<Compile Include="Messaging\Reflection\MessageDescriptionCollection.cs" />
<Compile Include="Mvc\OpenIdHelper.cs" />
<Compile Include="Mvc\OpenIdAjaxOptions.cs" />
diff --git a/src/DotNetOpenAuth/Messaging/Channel.cs b/src/DotNetOpenAuth/Messaging/Channel.cs
index 28babee..76dab88 100644
--- a/src/DotNetOpenAuth/Messaging/Channel.cs
+++ b/src/DotNetOpenAuth/Messaging/Channel.cs
@@ -1065,7 +1065,7 @@ namespace DotNetOpenAuth.Messaging {
Contract.Requires<ArgumentNullException>(message != null);
if (Logger.Channel.IsInfoEnabled) {
- var messageAccessor = this.MessageDescriptions.GetAccessor(message);
+ var messageAccessor = this.MessageDescriptions.GetAccessor(message, true);
Logger.Channel.InfoFormat(
"Processing incoming {0} ({1}) message:{2}{3}",
message.GetType().Name,
diff --git a/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs
index 3420de9..d47da4a 100644
--- a/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs
+++ b/src/DotNetOpenAuth/Messaging/IDirectWebRequestHandler.cs
@@ -82,7 +82,7 @@ namespace DotNetOpenAuth.Messaging {
/// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
/// <see cref="ProtocolException"/> to abstract away the transport and provide
/// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
- /// value, if set, shoud be Closed before throwing.</para>
+ /// value, if set, should be Closed before throwing.</para>
/// </remarks>
IncomingWebResponse GetResponse(HttpWebRequest request);
@@ -98,7 +98,7 @@ namespace DotNetOpenAuth.Messaging {
/// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
/// <see cref="ProtocolException"/> to abstract away the transport and provide
/// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
- /// value, if set, shoud be Closed before throwing.</para>
+ /// value, if set, should be Closed before throwing.</para>
/// </remarks>
IncomingWebResponse GetResponse(HttpWebRequest request, DirectWebRequestOptions options);
}
@@ -183,7 +183,7 @@ namespace DotNetOpenAuth.Messaging {
/// Implementations should catch <see cref="WebException"/> and wrap it in a
/// <see cref="ProtocolException"/> to abstract away the transport and provide
/// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
- /// value, if set, shoud be Closed before throwing.
+ /// value, if set, should be Closed before throwing.
/// </remarks>
IncomingWebResponse IDirectWebRequestHandler.GetResponse(HttpWebRequest request) {
Contract.Requires<ArgumentNullException>(request != null);
@@ -206,7 +206,7 @@ namespace DotNetOpenAuth.Messaging {
/// Implementations should catch <see cref="WebException"/> and wrap it in a
/// <see cref="ProtocolException"/> to abstract away the transport and provide
/// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
- /// value, if set, shoud be Closed before throwing.
+ /// value, if set, should be Closed before throwing.
/// </remarks>
IncomingWebResponse IDirectWebRequestHandler.GetResponse(HttpWebRequest request, DirectWebRequestOptions options) {
Contract.Requires<ArgumentNullException>(request != null);
diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
index cbd7f51..60fbdd8 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
@@ -1245,7 +1245,7 @@ namespace DotNetOpenAuth.Messaging {
/// by using appropriate character escaping.
/// </summary>
/// <param name="value">The untrusted string value to be escaped to protected against XSS attacks. May be null.</param>
- /// <returns>The escaped string.</returns>
+ /// <returns>The escaped string, surrounded by single-quotes.</returns>
internal static string GetSafeJavascriptValue(string value) {
if (value == null) {
return "null";
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartOriginalEncoder.cs b/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartOriginalEncoder.cs
new file mode 100644
index 0000000..9ad55c9
--- /dev/null
+++ b/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartOriginalEncoder.cs
@@ -0,0 +1,22 @@
+//-----------------------------------------------------------------------
+// <copyright file="IMessagePartOriginalEncoder.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging.Reflection {
+ /// <summary>
+ /// An interface describing how various objects can be serialized and deserialized between their object and string forms.
+ /// </summary>
+ /// <remarks>
+ /// Implementations of this interface must include a default constructor and must be thread-safe.
+ /// </remarks>
+ public interface IMessagePartOriginalEncoder : IMessagePartEncoder {
+ /// <summary>
+ /// Encodes the specified value as the original value that was formerly decoded.
+ /// </summary>
+ /// <param name="value">The value. Guaranteed to never be null.</param>
+ /// <returns>The <paramref name="value"/> in string form, ready for message transport.</returns>
+ string EncodeAsOriginalString(object value);
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs
index 53a5cdd..7dbab80 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs
@@ -82,7 +82,20 @@ namespace DotNetOpenAuth.Messaging.Reflection {
internal MessageDictionary GetDictionary(IMessage message) {
Contract.Requires<ArgumentNullException>(message != null);
Contract.Ensures(Contract.Result<MessageDictionary>() != null);
- return new MessageDictionary(message, this);
+ return this.GetDictionary(message, false);
+ }
+
+ /// <summary>
+ /// Gets a dictionary that provides read/write access to a message.
+ /// </summary>
+ /// <param name="message">The message the dictionary should provide access to.</param>
+ /// <param name="getOriginalValues">A value indicating whether this message dictionary will retrieve original values instead of normalized ones.</param>
+ /// <returns>The dictionary accessor to the message</returns>
+ [Pure]
+ internal MessageDictionary GetDictionary(IMessage message, bool getOriginalValues) {
+ Contract.Requires<ArgumentNullException>(message != null);
+ Contract.Ensures(Contract.Result<MessageDictionary>() != null);
+ return new MessageDictionary(message, this, getOriginalValues);
}
/// <summary>
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs
index 8911960..754b9d0 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs
@@ -106,7 +106,19 @@ namespace DotNetOpenAuth.Messaging.Reflection {
[Pure]
internal MessageDictionary GetAccessor(IMessage message) {
Contract.Requires<ArgumentNullException>(message != null);
- return this.Get(message).GetDictionary(message);
+ return this.GetAccessor(message, false);
+ }
+
+ /// <summary>
+ /// Gets the dictionary that provides read/write access to a message.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <param name="getOriginalValues">A value indicating whether this message dictionary will retrieve original values instead of normalized ones.</param>
+ /// <returns>The dictionary.</returns>
+ [Pure]
+ internal MessageDictionary GetAccessor(IMessage message, bool getOriginalValues) {
+ Contract.Requires<ArgumentNullException>(message != null);
+ return this.Get(message).GetDictionary(message, getOriginalValues);
}
/// <summary>
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
index f6eed24..2b60a9c 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
@@ -30,17 +30,24 @@ namespace DotNetOpenAuth.Messaging.Reflection {
private readonly MessageDescription description;
/// <summary>
+ /// Whether original string values should be retrieved instead of normalized ones.
+ /// </summary>
+ private readonly bool getOriginalValues;
+
+ /// <summary>
/// Initializes a new instance of the <see cref="MessageDictionary"/> class.
/// </summary>
/// <param name="message">The message instance whose values will be manipulated by this dictionary.</param>
/// <param name="description">The message description.</param>
+ /// <param name="getOriginalValues">A value indicating whether this message dictionary will retrieve original values instead of normalized ones.</param>
[Pure]
- internal MessageDictionary(IMessage message, MessageDescription description) {
+ internal MessageDictionary(IMessage message, MessageDescription description, bool getOriginalValues) {
Contract.Requires<ArgumentNullException>(message != null);
Contract.Requires<ArgumentNullException>(description != null);
this.message = message;
this.description = description;
+ this.getOriginalValues = getOriginalValues;
}
/// <summary>
@@ -103,7 +110,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
List<string> keys = new List<string>(this.description.Mapping.Count);
foreach (var pair in this.description.Mapping) {
// Don't include keys with null values, but default values for structs is ok
- if (pair.Value.GetValue(this.message) != null) {
+ if (pair.Value.GetValue(this.message, this.getOriginalValues) != null) {
keys.Add(pair.Key);
}
}
@@ -126,8 +133,8 @@ namespace DotNetOpenAuth.Messaging.Reflection {
get {
List<string> values = new List<string>(this.message.ExtraData.Count + this.description.Mapping.Count);
foreach (MessagePart part in this.description.Mapping.Values) {
- if (part.GetValue(this.message) != null) {
- values.Add(part.GetValue(this.message));
+ if (part.GetValue(this.message, this.getOriginalValues) != null) {
+ values.Add(part.GetValue(this.message, this.getOriginalValues));
}
}
@@ -167,7 +174,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
MessagePart part;
if (this.description.Mapping.TryGetValue(key, out part)) {
// Never throw KeyNotFoundException for declared properties.
- return part.GetValue(this.message);
+ return part.GetValue(this.message, this.getOriginalValues);
} else {
return this.message.ExtraData[key];
}
@@ -223,7 +230,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <returns>True if the parameter by the given name has a set value. False otherwise.</returns>
public bool ContainsKey(string key) {
return this.message.ExtraData.ContainsKey(key) ||
- (this.description.Mapping.ContainsKey(key) && this.description.Mapping[key].GetValue(this.message) != null);
+ (this.description.Mapping.ContainsKey(key) && this.description.Mapping[key].GetValue(this.message, this.getOriginalValues) != null);
}
/// <summary>
@@ -237,7 +244,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
} else {
MessagePart part;
if (this.description.Mapping.TryGetValue(key, out part)) {
- if (part.GetValue(this.message) != null) {
+ if (part.GetValue(this.message, this.getOriginalValues) != null) {
part.SetValue(this.message, null);
return true;
}
@@ -255,7 +262,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
public bool TryGetValue(string key, out string value) {
MessagePart part;
if (this.description.Mapping.TryGetValue(key, out part)) {
- value = part.GetValue(this.message);
+ value = part.GetValue(this.message, this.getOriginalValues);
return value != null;
}
return this.message.ExtraData.TryGetValue(key, out value);
@@ -306,7 +313,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
public bool Contains(KeyValuePair<string, string> item) {
MessagePart part;
if (this.description.Mapping.TryGetValue(item.Key, out part)) {
- return string.Equals(part.GetValue(this.message), item.Value, StringComparison.Ordinal);
+ return string.Equals(part.GetValue(this.message, this.getOriginalValues), item.Value, StringComparison.Ordinal);
} else {
return this.message.ExtraData.Contains(item);
}
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
index 6aa20e5..c1e7aa2 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
@@ -15,6 +15,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
using System.Net.Security;
using System.Reflection;
using System.Xml;
+ using DotNetOpenAuth.Configuration;
using DotNetOpenAuth.OpenId;
/// <summary>
@@ -90,15 +91,15 @@ namespace DotNetOpenAuth.Messaging.Reflection {
Contract.Assume(str != null);
return new Realm(str);
};
- Map<Uri>(uri => uri.AbsoluteUri, safeUri);
- Map<DateTime>(dt => XmlConvert.ToString(dt, XmlDateTimeSerializationMode.Utc), str => XmlConvert.ToDateTime(str, XmlDateTimeSerializationMode.Utc));
- Map<TimeSpan>(ts => ts.ToString(), str => TimeSpan.Parse(str));
- Map<byte[]>(safeFromByteArray, safeToByteArray);
- Map<Realm>(realm => realm.ToString(), safeRealm);
- Map<Identifier>(id => id.SerializedString, safeIdentifier);
- Map<bool>(value => value.ToString().ToLowerInvariant(), safeBool);
- Map<CultureInfo>(c => c.Name, str => new CultureInfo(str));
- Map<CultureInfo[]>(cs => string.Join(",", cs.Select(c => c.Name).ToArray()), str => str.Split(',').Select(s => new CultureInfo(s)).ToArray());
+ Map<Uri>(uri => uri.AbsoluteUri, uri => uri.OriginalString, safeUri);
+ Map<DateTime>(dt => XmlConvert.ToString(dt, XmlDateTimeSerializationMode.Utc), null, str => XmlConvert.ToDateTime(str, XmlDateTimeSerializationMode.Utc));
+ Map<TimeSpan>(ts => ts.ToString(), null, str => TimeSpan.Parse(str));
+ Map<byte[]>(safeFromByteArray, null, safeToByteArray);
+ Map<Realm>(realm => realm.ToString(), realm => realm.OriginalString, safeRealm);
+ Map<Identifier>(id => id.SerializedString, id => id.OriginalString, safeIdentifier);
+ Map<bool>(value => value.ToString().ToLowerInvariant(), null, safeBool);
+ Map<CultureInfo>(c => c.Name, null, str => new CultureInfo(str));
+ Map<CultureInfo[]>(cs => string.Join(",", cs.Select(c => c.Name).ToArray()), null, str => str.Split(',').Select(s => new CultureInfo(s)).ToArray());
}
/// <summary>
@@ -138,15 +139,18 @@ namespace DotNetOpenAuth.Messaging.Reflection {
if (converters.TryGetValue(underlyingType, out underlyingMapping)) {
this.converter = new ValueMapping(
underlyingMapping.ValueToString,
+ null,
str => str != null ? underlyingMapping.StringToValue(str) : null);
} else {
this.converter = new ValueMapping(
obj => obj != null ? obj.ToString() : null,
+ null,
str => str != null ? Convert.ChangeType(str, underlyingType, CultureInfo.InvariantCulture) : null);
}
} else {
this.converter = new ValueMapping(
obj => obj != null ? obj.ToString() : null,
+ null,
str => str != null ? Convert.ChangeType(str, this.memberDeclaredType, CultureInfo.InvariantCulture) : null);
}
}
@@ -229,7 +233,8 @@ namespace DotNetOpenAuth.Messaging.Reflection {
try {
if (this.IsConstantValue) {
string constantValue = this.GetValue(message);
- if (!string.Equals(constantValue, value)) {
+ var caseSensitivity = DotNetOpenAuthSection.Configuration.Messaging.Strict ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
+ if (!string.Equals(constantValue, value, caseSensitivity)) {
throw new ArgumentException(string.Format(
CultureInfo.CurrentCulture,
MessagingStrings.UnexpectedMessagePartValueForConstant,
@@ -251,7 +256,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
}
/// <summary>
- /// Gets the value of a member of a given message.
+ /// Gets the normalized form of a value of a member of a given message.
/// Used in serialization.
/// </summary>
/// <param name="message">The message instance to read the value from.</param>
@@ -259,7 +264,23 @@ namespace DotNetOpenAuth.Messaging.Reflection {
internal string GetValue(IMessage message) {
try {
object value = this.GetValueAsObject(message);
- return this.ToString(value);
+ return this.ToString(value, false);
+ } catch (FormatException ex) {
+ throw ErrorUtilities.Wrap(ex, MessagingStrings.MessagePartWriteFailure, message.GetType(), this.Name);
+ }
+ }
+
+ /// <summary>
+ /// Gets the value of a member of a given message.
+ /// Used in serialization.
+ /// </summary>
+ /// <param name="message">The message instance to read the value from.</param>
+ /// <param name="originalValue">A value indicating whether the original value should be retrieved (as opposed to a normalized form of it).</param>
+ /// <returns>The string representation of the member's value.</returns>
+ internal string GetValue(IMessage message, bool originalValue) {
+ try {
+ object value = this.GetValueAsObject(message);
+ return this.ToString(value, originalValue);
} catch (FormatException ex) {
throw ErrorUtilities.Wrap(ex, MessagingStrings.MessagePartWriteFailure, message.GetType(), this.Name);
}
@@ -296,11 +317,20 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// </summary>
/// <typeparam name="T">The custom type to convert to and from strings.</typeparam>
/// <param name="toString">The function to convert the custom type to a string.</param>
+ /// <param name="toOriginalString">The mapping function that converts some custom value to its original (non-normalized) string. May be null if the same as the <paramref name="toString"/> function.</param>
/// <param name="toValue">The function to convert a string to the custom type.</param>
- private static void Map<T>(Func<T, string> toString, Func<string, T> toValue) {
+ private static void Map<T>(Func<T, string> toString, Func<T, string> toOriginalString, Func<string, T> toValue) {
+ Contract.Requires<ArgumentNullException>(toString != null, "toString");
+ Contract.Requires<ArgumentNullException>(toValue != null, "toValue");
+
+ if (toOriginalString == null) {
+ toOriginalString = toString;
+ }
+
Func<object, string> safeToString = obj => obj != null ? toString((T)obj) : null;
+ Func<object, string> safeToOriginalString = obj => obj != null ? toOriginalString((T)obj) : null;
Func<string, object> safeToT = str => str != null ? toValue(str) : default(T);
- converters.Add(typeof(T), new ValueMapping(safeToString, safeToT));
+ converters.Add(typeof(T), new ValueMapping(safeToString, safeToOriginalString, safeToT));
}
/// <summary>
@@ -352,11 +382,12 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// Converts the member's value to its string representation.
/// </summary>
/// <param name="value">The value of the member.</param>
+ /// <param name="originalString">A value indicating whether a string matching the originally decoded string should be returned (as opposed to a normalized string).</param>
/// <returns>
/// The string representation of the member's value.
/// </returns>
- private string ToString(object value) {
- return this.converter.ValueToString(value);
+ private string ToString(object value, bool originalString) {
+ return originalString ? this.converter.ValueToOriginalString(value) : this.converter.ValueToString(value);
}
/// <summary>
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs b/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs
index 1c7631e..b0b8b47 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs
@@ -19,6 +19,12 @@ namespace DotNetOpenAuth.Messaging.Reflection {
internal readonly Func<object, string> ValueToString;
/// <summary>
+ /// The mapping function that converts some custom type to the original string
+ /// (possibly non-normalized) that represents it.
+ /// </summary>
+ internal readonly Func<object, string> ValueToOriginalString;
+
+ /// <summary>
/// The mapping function that converts a string to some custom type.
/// </summary>
internal readonly Func<string, object> StringToValue;
@@ -26,13 +32,15 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <summary>
/// Initializes a new instance of the <see cref="ValueMapping"/> struct.
/// </summary>
- /// <param name="toString">The mapping function that converts some custom type to a string.</param>
- /// <param name="toValue">The mapping function that converts a string to some custom type.</param>
- internal ValueMapping(Func<object, string> toString, Func<string, object> toValue) {
+ /// <param name="toString">The mapping function that converts some custom value to a string.</param>
+ /// <param name="toOriginalString">The mapping function that converts some custom value to its original (non-normalized) string. May be null if the same as the <paramref name="toString"/> function.</param>
+ /// <param name="toValue">The mapping function that converts a string to some custom value.</param>
+ internal ValueMapping(Func<object, string> toString, Func<object, string> toOriginalString, Func<string, object> toValue) {
Contract.Requires<ArgumentNullException>(toString != null);
Contract.Requires<ArgumentNullException>(toValue != null);
this.ValueToString = toString;
+ this.ValueToOriginalString = toOriginalString ?? toString;
this.StringToValue = toValue;
}
@@ -45,8 +53,15 @@ namespace DotNetOpenAuth.Messaging.Reflection {
var nullEncoder = encoder as IMessagePartNullEncoder;
string nullString = nullEncoder != null ? nullEncoder.EncodedNullValue : null;
+ var originalStringEncoder = encoder as IMessagePartOriginalEncoder;
+ Func<object, string> originalStringEncode = encoder.Encode;
+ if (originalStringEncoder != null) {
+ originalStringEncode = originalStringEncoder.EncodeAsOriginalString;
+ }
+
this.ValueToString = obj => (obj != null) ? encoder.Encode(obj) : nullString;
this.StringToValue = str => (str != null) ? encoder.Decode(str) : null;
+ this.ValueToOriginalString = obj => (obj != null) ? originalStringEncode(obj) : nullString;
}
}
}
diff --git a/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs
index 440f5f6..a5725cd 100644
--- a/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs
+++ b/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs
@@ -95,7 +95,7 @@ namespace DotNetOpenAuth.Messaging {
/// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
/// <see cref="ProtocolException"/> to abstract away the transport and provide
/// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
- /// value, if set, shoud be Closed before throwing.</para>
+ /// value, if set, should be Closed before throwing.</para>
/// </remarks>
public IncomingWebResponse GetResponse(HttpWebRequest request) {
return this.GetResponse(request, DirectWebRequestOptions.None);
@@ -115,7 +115,7 @@ namespace DotNetOpenAuth.Messaging {
/// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
/// <see cref="ProtocolException"/> to abstract away the transport and provide
/// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
- /// value, if set, shoud be Closed before throwing.</para>
+ /// value, if set, should be Closed before throwing.</para>
/// </remarks>
public IncomingWebResponse GetResponse(HttpWebRequest request, DirectWebRequestOptions options) {
// This request MAY have already been prepared by GetRequestStream, but
diff --git a/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs
index 3ea1bf2..838b7e8 100644
--- a/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs
+++ b/src/DotNetOpenAuth/Messaging/UntrustedWebRequestHandler.cs
@@ -231,7 +231,7 @@ namespace DotNetOpenAuth.Messaging {
/// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
/// <see cref="ProtocolException"/> to abstract away the transport and provide
/// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
- /// value, if set, shoud be Closed before throwing.</para>
+ /// value, if set, should be Closed before throwing.</para>
/// </remarks>
[SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Uri(Uri, string) accepts second arguments that Uri(Uri, new Uri(string)) does not that we must support.")]
public IncomingWebResponse GetResponse(HttpWebRequest request, DirectWebRequestOptions options) {
@@ -298,7 +298,7 @@ namespace DotNetOpenAuth.Messaging {
/// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
/// <see cref="ProtocolException"/> to abstract away the transport and provide
/// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
- /// value, if set, shoud be Closed before throwing.</para>
+ /// value, if set, should be Closed before throwing.</para>
/// </remarks>
IncomingWebResponse IDirectWebRequestHandler.GetResponse(HttpWebRequest request) {
return this.GetResponse(request, DirectWebRequestOptions.None);
diff --git a/src/DotNetOpenAuth/Mvc/OpenIdAjaxOptions.cs b/src/DotNetOpenAuth/Mvc/OpenIdAjaxOptions.cs
index 9956966..4b88d04 100644
--- a/src/DotNetOpenAuth/Mvc/OpenIdAjaxOptions.cs
+++ b/src/DotNetOpenAuth/Mvc/OpenIdAjaxOptions.cs
@@ -7,8 +7,10 @@
namespace DotNetOpenAuth.Mvc {
using System;
using System.Collections.Generic;
+ using System.Globalization;
using System.Linq;
using System.Text;
+ using DotNetOpenAuth.Messaging;
/// <summary>
/// A set of customizations available for the scripts sent to the browser in AJAX OpenID scenarios.
@@ -41,6 +43,14 @@ namespace DotNetOpenAuth.Mvc {
public int FormIndex { get; set; }
/// <summary>
+ /// Gets or sets the id of the form in the document.forms array on the browser that should
+ /// be submitted when the user is ready to send the positive assertion to the RP. A value
+ /// in this property takes precedence over any value in the <see cref="FormIndex"/> property.
+ /// </summary>
+ /// <value>The form id.</value>
+ public string FormId { get; set; }
+
+ /// <summary>
/// Gets or sets the preloaded discovery results.
/// </summary>
public string PreloadedDiscoveryResults { get; set; }
@@ -55,5 +65,12 @@ namespace DotNetOpenAuth.Mvc {
/// asynchronous authentication of the user for diagnostic purposes.
/// </summary>
public bool ShowDiagnosticIFrame { get; set; }
+
+ /// <summary>
+ /// Gets the form key to use when accessing the relevant form.
+ /// </summary>
+ internal string FormKey {
+ get { return string.IsNullOrEmpty(this.FormId) ? this.FormIndex.ToString(CultureInfo.InvariantCulture) : MessagingUtilities.GetSafeJavascriptValue(this.FormId); }
+ }
}
}
diff --git a/src/DotNetOpenAuth/Mvc/OpenIdHelper.cs b/src/DotNetOpenAuth/Mvc/OpenIdHelper.cs
index f276019..193e445 100644
--- a/src/DotNetOpenAuth/Mvc/OpenIdHelper.cs
+++ b/src/DotNetOpenAuth/Mvc/OpenIdHelper.cs
@@ -151,7 +151,7 @@ window.openid_trace = {1}; // causes lots of messages";
blockFormat,
additionalOptions.AssertionHiddenFieldId,
additionalOptions.ReturnUrlHiddenFieldId,
- additionalOptions.FormIndex);
+ additionalOptions.FormKey);
blockFormat = @" $(function () {{
var box = document.getElementsByName('openid_identifier')[0];
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs
index f178647..55c2dc5 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs
@@ -30,6 +30,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI {
/// whether to use a standard full window redirect or a popup) via the
/// <see cref="IdentifierDiscoveryResult.IsExtensionSupported&lt;T&gt;()"/> method.</para>
/// </remarks>
+ [Serializable]
public sealed class UIRequest : IOpenIdMessageExtension, IMessageWithEvents {
/// <summary>
/// The factory method that may be used in deserialization of this message.
diff --git a/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs
index 5306c54..db69d3d 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs
@@ -45,7 +45,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
// Copy all message parts from the id_res message into this one,
// except for the openid.mode parameter.
- MessageDictionary checkPayload = channel.MessageDescriptions.GetAccessor(message);
+ MessageDictionary checkPayload = channel.MessageDescriptions.GetAccessor(message, true);
MessageDictionary thisPayload = channel.MessageDescriptions.GetAccessor(this);
foreach (var pair in checkPayload) {
if (!string.Equals(pair.Key, this.Protocol.openid.mode)) {
diff --git a/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationResponse.cs
index 61825e8..f1bb5ac 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationResponse.cs
@@ -47,7 +47,7 @@ namespace DotNetOpenAuth.OpenId.Messages {
// really doesn't exist. OpenID 2.0 section 11.4.2.2.
IndirectSignedResponse signedResponse = new IndirectSignedResponse(request, provider.Channel);
string invalidateHandle = ((ITamperResistantOpenIdMessage)signedResponse).InvalidateHandle;
- if (invalidateHandle != null && provider.AssociationStore.GetAssociation(AssociationRelyingPartyType.Smart, invalidateHandle) == null) {
+ if (!string.IsNullOrEmpty(invalidateHandle) && provider.AssociationStore.GetAssociation(AssociationRelyingPartyType.Smart, invalidateHandle) == null) {
this.InvalidateHandle = invalidateHandle;
}
}
@@ -70,8 +70,10 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// <para>This two-step process for invalidating associations is necessary
/// to prevent an attacker from invalidating an association at will by
/// adding "invalidate_handle" parameters to an authentication response.</para>
+ /// <para>For OpenID 1.1, we allow this to be present but empty to put up with poor implementations such as Blogger.</para>
/// </remarks>
- [MessagePart("invalidate_handle", IsRequired = false, AllowEmpty = false)]
+ [MessagePart("invalidate_handle", IsRequired = false, AllowEmpty = true, MaxVersion = "1.1")]
+ [MessagePart("invalidate_handle", IsRequired = false, AllowEmpty = false, MinVersion = "2.0")]
internal string InvalidateHandle { get; set; }
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs
index 2f02974..fff4cf6 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs
@@ -207,7 +207,11 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// Gets or sets the association handle that the Provider wants the Relying Party to not use any more.
/// </summary>
/// <value>If the Relying Party sent an invalid association handle with the request, it SHOULD be included here.</value>
- [MessagePart("openid.invalidate_handle", IsRequired = false, AllowEmpty = false)]
+ /// <remarks>
+ /// For OpenID 1.1, we allow this to be present but empty to put up with poor implementations such as Blogger.
+ /// </remarks>
+ [MessagePart("openid.invalidate_handle", IsRequired = false, AllowEmpty = true, MaxVersion = "1.1")]
+ [MessagePart("openid.invalidate_handle", IsRequired = false, AllowEmpty = false, MinVersion = "2.0")]
string ITamperResistantOpenIdMessage.InvalidateHandle { get; set; }
/// <summary>
diff --git a/src/DotNetOpenAuth/OpenId/Realm.cs b/src/DotNetOpenAuth/OpenId/Realm.cs
index 4137c72..98e3598 100644
--- a/src/DotNetOpenAuth/OpenId/Realm.cs
+++ b/src/DotNetOpenAuth/OpenId/Realm.cs
@@ -180,6 +180,14 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
+ /// Gets the original string.
+ /// </summary>
+ /// <value>The original string.</value>
+ internal string OriginalString {
+ get { return this.uri.OriginalString; }
+ }
+
+ /// <summary>
/// Gets the realm URL. If the realm includes a wildcard, it is not included here.
/// </summary>
internal Uri NoWildcardUri {
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.js b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.js
index c58e06e..297ea23 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.js
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdSelector.js
@@ -178,15 +178,19 @@ $(function() {
ajaxbox.focus();
}
});
+
+ $(ajaxbox.form).keydown(function(e) {
+ if (e.keyCode == $.ui.keyCode.ENTER) {
+ // we do NOT want to submit the form on ENTER.
+ e.preventDefault();
+ }
+ });
}
// Make popup window close on escape (the dialog style is already taken care of)
$(document).keydown(function(e) {
if (e.keyCode == $.ui.keyCode.ESCAPE) {
window.close();
- } else if (e.keyCode == $.ui.keyCode.ENTER) {
- // we do NOT want to submit the form on ENTER.
- e.preventDefault();
}
});
}); \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/UriIdentifier.cs b/src/DotNetOpenAuth/OpenId/UriIdentifier.cs
index 278ae37..639ff57 100644
--- a/src/DotNetOpenAuth/OpenId/UriIdentifier.cs
+++ b/src/DotNetOpenAuth/OpenId/UriIdentifier.cs
@@ -616,6 +616,21 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
+ /// Returns a hash code for this instance.
+ /// </summary>
+ /// <returns>
+ /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
+ /// </returns>
+ public override int GetHashCode() {
+ int hashCode = 0;
+ hashCode += StringComparer.OrdinalIgnoreCase.GetHashCode(this.Scheme);
+ hashCode += StringComparer.OrdinalIgnoreCase.GetHashCode(this.Authority);
+ hashCode += StringComparer.Ordinal.GetHashCode(this.Path);
+ hashCode += StringComparer.Ordinal.GetHashCode(this.Query);
+ return hashCode;
+ }
+
+ /// <summary>
/// Normalizes the characters that are escaped in the given URI path.
/// </summary>
/// <param name="path">The path to normalize.</param>