summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj14
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/Reflection/MessagePartTests.cs7
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/Reflection/ValueMappingTests.cs4
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperOPTests.cs18
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Extensions/UI/UIRequestTests.cs19
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Messages/CheckAuthenticationRequestTests.cs31
-rw-r--r--src/DotNetOpenAuth.sln84
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj4
-rw-r--r--src/DotNetOpenAuth/Messaging/Channel.cs2
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingUtilities.cs5
-rw-r--r--src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs4
-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.cs81
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs21
-rw-r--r--src/DotNetOpenAuth/OpenId/AssociationMemoryStore.cs39
-rw-r--r--src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs7
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs7
-rw-r--r--src/DotNetOpenAuth/OpenId/IAssociationStore.cs17
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/StandardProviderApplicationStore.cs13
-rw-r--r--src/DotNetOpenAuth/OpenId/Realm.cs8
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs24
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs13
-rw-r--r--src/DotNetOpenAuth/OpenId/UriIdentifier.cs15
-rw-r--r--src/version.txt2
29 files changed, 320 insertions, 199 deletions
diff --git a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj
index 6355d59..df29b1f 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/MessagePartTests.cs b/src/DotNetOpenAuth.Test/Messaging/Reflection/MessagePartTests.cs
index 524f440..66237e7 100644
--- a/src/DotNetOpenAuth.Test/Messaging/Reflection/MessagePartTests.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/Reflection/MessagePartTests.cs
@@ -28,7 +28,12 @@ namespace DotNetOpenAuth.Test.Messaging.Reflection {
[TestCase]
public void OptionalNullableStruct() {
- this.ParameterizedMessageTypeTest(typeof(MessageWithNullableOptionalStruct));
+ var message = new MessageWithNullableOptionalStruct();
+ var part = this.ParameterizedMessageTypeTest(message.GetType());
+
+ Assert.IsNull(part.GetValue(message));
+ part.SetValue(message, "3");
+ Assert.AreEqual("3", part.GetValue(message));
}
[TestCase]
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/Extensions/ExtensionsInteropHelperOPTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperOPTests.cs
index 2206c79..1b1dd49 100644
--- a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperOPTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperOPTests.cs
@@ -42,6 +42,8 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions {
Assert.IsNull(sreg);
// Make sure we're still able to send an sreg response.
+ // (not really a valid scenario, since OPs don't have public access
+ // to directly create a response without a request.
var sregResponse = new ClaimsResponse();
this.request.AddResponseExtension(sregResponse);
ExtensionsInteropHelper.ConvertSregToMatchRequest(this.request);
@@ -60,7 +62,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions {
/// </summary>
[TestCase]
public void UnifyExtensionsAsSregWithSreg() {
- var sregInjected = new ClaimsRequest {
+ var sregInjected = new ClaimsRequest(DotNetOpenAuth.OpenId.Extensions.SimpleRegistration.Constants.sreg_ns) {
Nickname = DemandLevel.Request,
};
this.extensions.Add(sregInjected);
@@ -69,7 +71,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions {
Assert.AreEqual(DemandLevel.Request, sreg.Nickname);
Assert.AreEqual(DemandLevel.NoRequest, sreg.FullName);
- var sregResponse = new ClaimsResponse();
+ var sregResponse = sreg.CreateResponse();
this.request.AddResponseExtension(sregResponse);
ExtensionsInteropHelper.ConvertSregToMatchRequest(this.request);
var extensions = this.GetResponseExtensions();
@@ -97,7 +99,7 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions {
/// </summary>
[TestCase]
public void UnifyExtensionsAsSregWithBothSregAndAX() {
- var sregInjected = new ClaimsRequest {
+ var sregInjected = new ClaimsRequest(DotNetOpenAuth.OpenId.Extensions.SimpleRegistration.Constants.sreg_ns) {
Nickname = DemandLevel.Request,
};
this.extensions.Add(sregInjected);
@@ -109,9 +111,8 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions {
Assert.AreEqual(DemandLevel.Request, sreg.Nickname);
Assert.AreEqual(DemandLevel.NoRequest, sreg.Email);
- var sregResponseInjected = new ClaimsResponse {
- Nickname = "andy",
- };
+ var sregResponseInjected = sreg.CreateResponse();
+ sregResponseInjected.Nickname = "andy";
this.request.AddResponseExtension(sregResponseInjected);
var axResponseInjected = new FetchResponse();
axResponseInjected.Attributes.Add(WellKnownAttributes.Contact.Email, "a@b.com");
@@ -140,9 +141,8 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions {
Assert.AreEqual(DemandLevel.Require, sreg.FullName);
Assert.AreEqual(DemandLevel.NoRequest, sreg.Language);
- var sregResponse = new ClaimsResponse {
- Nickname = "andy",
- };
+ var sregResponse = sreg.CreateResponse();
+ sregResponse.Nickname = "andy";
this.request.AddResponseExtension(sregResponse);
ExtensionsInteropHelper.ConvertSregToMatchRequest(this.request);
var extensions = this.GetResponseExtensions();
diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/UI/UIRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/UI/UIRequestTests.cs
index c216a4d..389fa5d 100644
--- a/src/DotNetOpenAuth.Test/OpenId/Extensions/UI/UIRequestTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/UI/UIRequestTests.cs
@@ -14,10 +14,27 @@ namespace DotNetOpenAuth.Test.OpenId.Extensions.UI {
public class UIRequestTests : OpenIdTestBase {
[TestCase]
public void Defaults() {
- UIRequest request = new UIRequest();
+ var request = new UIRequest();
Assert.AreEqual("popup", request.Mode);
Assert.AreEqual(1, request.LanguagePreference.Length);
Assert.AreEqual(CultureInfo.CurrentUICulture, request.LanguagePreference[0]);
+ Assert.IsFalse(request.Icon.HasValue);
+ }
+
+ [TestCase]
+ public void IconEncodingDecoding()
+ {
+ var request = new UIRequest();
+ MessageDictionary dictionary = this.MessageDescriptions.GetAccessor(request);
+ Assert.IsFalse(dictionary.ContainsKey("icon"));
+
+ Assert.IsFalse(request.Icon.HasValue);
+ dictionary["icon"] = "true";
+ Assert.IsTrue(request.Icon.Value);
+
+ dictionary.ClearValues();
+ request.Icon = true;
+ Assert.AreEqual("true", dictionary["icon"]);
}
[TestCase]
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.sln b/src/DotNetOpenAuth.sln
index 55d43e8..f6f3d03 100644
--- a/src/DotNetOpenAuth.sln
+++ b/src/DotNetOpenAuth.sln
@@ -119,54 +119,6 @@ Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "OpenIdRelyingPartyClassicAs
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OAuthConsumerWpf", "..\samples\OAuthConsumerWpf\OAuthConsumerWpf.csproj", "{6EC36418-DBC5-4AD1-A402-413604AA7A08}"
- ProjectSection(ProjectDependencies) = postProject
- {7ADCCD5C-AC2B-4340-9410-FE3A31A48191} = {7ADCCD5C-AC2B-4340-9410-FE3A31A48191}
- EndProjectSection
-EndProject
-Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "OAuthConsumer", "..\samples\OAuthConsumer\", "{9ADBE36D-9960-48F6-82E9-B4AC559E9AC3}"
- ProjectSection(WebsiteProperties) = preProject
- TargetFrameworkMoniker = ".NETFramework,Version%3Dv3.5"
- ProjectReferences = "{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}|DotNetOpenAuth.dll;{AA78D112-D889-414B-A7D4-467B34C7B663}|DotNetOpenAuth.ApplicationBlock.dll;"
- Debug.AspNetCompiler.VirtualPath = "/OAuthConsumer"
- Debug.AspNetCompiler.PhysicalPath = "..\samples\OAuthConsumer\"
- Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\OAuthConsumer\"
- Debug.AspNetCompiler.Updateable = "true"
- Debug.AspNetCompiler.ForceOverwrite = "true"
- Debug.AspNetCompiler.FixedNames = "false"
- Debug.AspNetCompiler.Debug = "True"
- Release.AspNetCompiler.VirtualPath = "/OAuthConsumer"
- Release.AspNetCompiler.PhysicalPath = "..\samples\OAuthConsumer\"
- Release.AspNetCompiler.TargetPath = "PrecompiledWeb\OAuthConsumer\"
- Release.AspNetCompiler.Updateable = "true"
- Release.AspNetCompiler.ForceOverwrite = "true"
- Release.AspNetCompiler.FixedNames = "false"
- Release.AspNetCompiler.Debug = "False"
- VWDPort = "59721"
- StartServerOnDebug = "false"
- EndProjectSection
-EndProject
-Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "OAuthServiceProvider", "..\samples\OAuthServiceProvider\", "{7ADCCD5C-AC2B-4340-9410-FE3A31A48191}"
- ProjectSection(WebsiteProperties) = preProject
- TargetFrameworkMoniker = ".NETFramework,Version%3Dv3.5"
- ProjectReferences = "{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}|DotNetOpenAuth.dll;"
- Debug.AspNetCompiler.VirtualPath = "/OAuthServiceProvider"
- Debug.AspNetCompiler.PhysicalPath = "..\samples\OAuthServiceProvider\"
- Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\OAuthServiceProvider\"
- Debug.AspNetCompiler.Updateable = "true"
- Debug.AspNetCompiler.ForceOverwrite = "true"
- Debug.AspNetCompiler.FixedNames = "false"
- Debug.AspNetCompiler.Debug = "True"
- Release.AspNetCompiler.VirtualPath = "/OAuthServiceProvider"
- Release.AspNetCompiler.PhysicalPath = "..\samples\OAuthServiceProvider\"
- Release.AspNetCompiler.TargetPath = "PrecompiledWeb\OAuthServiceProvider\"
- Release.AspNetCompiler.Updateable = "true"
- Release.AspNetCompiler.ForceOverwrite = "true"
- Release.AspNetCompiler.FixedNames = "false"
- Release.AspNetCompiler.Debug = "False"
- VWDPort = "65169"
- VWDDynamicPort = "false"
- StartServerOnDebug = "false"
- EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdOfflineProvider", "..\samples\OpenIdOfflineProvider\OpenIdOfflineProvider.csproj", "{5C65603B-235F-47E6-B536-06385C60DE7F}"
EndProject
@@ -187,6 +139,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdWebRingSsoProvider",
EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "OpenIdRelyingPartyWebFormsVB", "..\samples\OpenIdRelyingPartyWebFormsVB\OpenIdRelyingPartyWebFormsVB.vbproj", "{F289B925-4307-4BEC-B411-885CE70E3379}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OAuthConsumer", "..\samples\OAuthConsumer\OAuthConsumer.csproj", "{9529606E-AF76-4387-BFB7-3D10A5B399AA}"
+ ProjectSection(ProjectDependencies) = postProject
+ {E135F455-0669-49F8-9207-07FCA8C8FC79} = {E135F455-0669-49F8-9207-07FCA8C8FC79}
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OAuthServiceProvider", "..\samples\OAuthServiceProvider\OAuthServiceProvider.csproj", "{E135F455-0669-49F8-9207-07FCA8C8FC79}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
CodeAnalysis|Any CPU = CodeAnalysis|Any CPU
@@ -260,18 +219,6 @@ Global
{6EC36418-DBC5-4AD1-A402-413604AA7A08}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6EC36418-DBC5-4AD1-A402-413604AA7A08}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6EC36418-DBC5-4AD1-A402-413604AA7A08}.Release|Any CPU.Build.0 = Release|Any CPU
- {9ADBE36D-9960-48F6-82E9-B4AC559E9AC3}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU
- {9ADBE36D-9960-48F6-82E9-B4AC559E9AC3}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU
- {9ADBE36D-9960-48F6-82E9-B4AC559E9AC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {9ADBE36D-9960-48F6-82E9-B4AC559E9AC3}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {9ADBE36D-9960-48F6-82E9-B4AC559E9AC3}.Release|Any CPU.ActiveCfg = Debug|Any CPU
- {9ADBE36D-9960-48F6-82E9-B4AC559E9AC3}.Release|Any CPU.Build.0 = Debug|Any CPU
- {7ADCCD5C-AC2B-4340-9410-FE3A31A48191}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU
- {7ADCCD5C-AC2B-4340-9410-FE3A31A48191}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU
- {7ADCCD5C-AC2B-4340-9410-FE3A31A48191}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7ADCCD5C-AC2B-4340-9410-FE3A31A48191}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7ADCCD5C-AC2B-4340-9410-FE3A31A48191}.Release|Any CPU.ActiveCfg = Debug|Any CPU
- {7ADCCD5C-AC2B-4340-9410-FE3A31A48191}.Release|Any CPU.Build.0 = Debug|Any CPU
{5C65603B-235F-47E6-B536-06385C60DE7F}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU
{5C65603B-235F-47E6-B536-06385C60DE7F}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU
{5C65603B-235F-47E6-B536-06385C60DE7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
@@ -323,6 +270,18 @@ Global
{F289B925-4307-4BEC-B411-885CE70E3379}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F289B925-4307-4BEC-B411-885CE70E3379}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F289B925-4307-4BEC-B411-885CE70E3379}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9529606E-AF76-4387-BFB7-3D10A5B399AA}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU
+ {9529606E-AF76-4387-BFB7-3D10A5B399AA}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU
+ {9529606E-AF76-4387-BFB7-3D10A5B399AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9529606E-AF76-4387-BFB7-3D10A5B399AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9529606E-AF76-4387-BFB7-3D10A5B399AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9529606E-AF76-4387-BFB7-3D10A5B399AA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E135F455-0669-49F8-9207-07FCA8C8FC79}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU
+ {E135F455-0669-49F8-9207-07FCA8C8FC79}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU
+ {E135F455-0669-49F8-9207-07FCA8C8FC79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E135F455-0669-49F8-9207-07FCA8C8FC79}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E135F455-0669-49F8-9207-07FCA8C8FC79}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E135F455-0669-49F8-9207-07FCA8C8FC79}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -340,11 +299,10 @@ Global
{BBACD972-014D-478F-9B07-56B9E1D4CC73} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1}
{B64A1E7E-6A15-4B91-AF13-7D48F7DA5942} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1}
{0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1}
- {6EB90284-BD15-461C-BBF2-131CF55F7C8B} = {8A5CEDB9-7F8A-4BE2-A1B9-97130F453277}
{F289B925-4307-4BEC-B411-885CE70E3379} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1}
{6EC36418-DBC5-4AD1-A402-413604AA7A08} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6}
- {9ADBE36D-9960-48F6-82E9-B4AC559E9AC3} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6}
- {7ADCCD5C-AC2B-4340-9410-FE3A31A48191} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6}
+ {9529606E-AF76-4387-BFB7-3D10A5B399AA} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6}
+ {E135F455-0669-49F8-9207-07FCA8C8FC79} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6}
{6EB90284-BD15-461C-BBF2-131CF55F7C8B} = {8A5CEDB9-7F8A-4BE2-A1B9-97130F453277}
{5C65603B-235F-47E6-B536-06385C60DE7F} = {E9ED920D-1F83-48C0-9A4B-09CCE505FE6D}
{A78F8FC6-7B03-4230-BE41-761E400D6810} = {B9EB8729-4B54-4453-B089-FE6761BA3057}
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
index 1c018eb..919d1f2 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>
@@ -197,7 +196,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">
@@ -208,7 +206,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' " />
@@ -301,6 +298,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 055ce68..cc411c8 100644
--- a/src/DotNetOpenAuth/Messaging/Channel.cs
+++ b/src/DotNetOpenAuth/Messaging/Channel.cs
@@ -978,7 +978,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/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
index 231637a..5dccc7a 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
@@ -780,7 +780,10 @@ namespace DotNetOpenAuth.Messaging {
if (throwOnNullKey) {
throw new ArgumentException(MessagingStrings.UnexpectedNullKey);
} else {
- Logger.OpenId.WarnFormat("Null key with value {0} encountered while translating NameValueCollection to Dictionary.", nvc[key]);
+ // Only emit a warning if there was a non-empty value.
+ if (!string.IsNullOrEmpty(nvc[key])) {
+ Logger.OpenId.WarnFormat("Null key with value {0} encountered while translating NameValueCollection to Dictionary.", nvc[key]);
+ }
}
} else {
dictionary.Add(key, nvc[key]);
diff --git a/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs b/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs
index 6ccc1cb..cc655cf 100644
--- a/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs
+++ b/src/DotNetOpenAuth/Messaging/OutgoingWebResponse.cs
@@ -121,7 +121,7 @@ namespace DotNetOpenAuth.Messaging {
/// Automatically sends the appropriate response to the user agent
/// and ends execution on the current page or handler.
/// </summary>
- /// <exception cref="ThreadAbortException">Thrown by ASP.NET in order to prevent additional data from the page being sent to the client and corrupting the response.</exception>
+ /// <exception cref="ThreadAbortException">Typically thrown by ASP.NET in order to prevent additional data from the page being sent to the client and corrupting the response.</exception>
/// <remarks>
/// Requires a current HttpContext.
/// </remarks>
@@ -137,7 +137,7 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="context">The context of the HTTP request whose response should be set.
/// Typically this is <see cref="HttpContext.Current"/>.</param>
- /// <exception cref="ThreadAbortException">Thrown by ASP.NET in order to prevent additional data from the page being sent to the client and corrupting the response.</exception>
+ /// <exception cref="ThreadAbortException">Typically thrown by ASP.NET in order to prevent additional data from the page being sent to the client and corrupting the response.</exception>
public virtual void Send(HttpContext context) {
Contract.Requires<ArgumentNullException>(context != 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 5493ba6..3b41b35 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs
@@ -66,7 +66,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 ff8b74b..125742c 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs
@@ -78,7 +78,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 8ccad22..b876ec4 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
@@ -91,14 +91,14 @@ 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<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<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>
@@ -130,9 +130,28 @@ namespace DotNetOpenAuth.Messaging.Reflection {
Contract.Assume(this.memberDeclaredType != null); // CC missing PropertyInfo.PropertyType ensures result != null
if (attribute.Encoder == null) {
if (!converters.TryGetValue(this.memberDeclaredType, out this.converter)) {
- this.converter = new ValueMapping(
- obj => obj != null ? obj.ToString() : null,
- str => str != null ? Convert.ChangeType(str, this.memberDeclaredType, CultureInfo.InvariantCulture) : null);
+ if (this.memberDeclaredType.IsGenericType &&
+ this.memberDeclaredType.GetGenericTypeDefinition() == typeof(Nullable<>)) {
+ // It's a nullable type. Try again to look up an appropriate converter for the underlying type.
+ Type underlyingType = Nullable.GetUnderlyingType(this.memberDeclaredType);
+ ValueMapping underlyingMapping;
+ 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);
+ }
}
} else {
this.converter = new ValueMapping(GetEncoder(attribute.Encoder));
@@ -213,7 +232,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>
@@ -221,7 +240,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);
}
@@ -254,15 +289,24 @@ namespace DotNetOpenAuth.Messaging.Reflection {
}
/// <summary>
- /// Adds a pair of type conversion functions to the static converstion map.
+ /// Adds a pair of type conversion functions to the static conversion map.
/// </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>
@@ -314,11 +358,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/OpenId/AssociationMemoryStore.cs b/src/DotNetOpenAuth/OpenId/AssociationMemoryStore.cs
index 13633b4..7a20fd5 100644
--- a/src/DotNetOpenAuth/OpenId/AssociationMemoryStore.cs
+++ b/src/DotNetOpenAuth/OpenId/AssociationMemoryStore.cs
@@ -20,12 +20,22 @@ namespace DotNetOpenAuth.OpenId {
/// </remarks>
internal class AssociationMemoryStore<TKey> : IAssociationStore<TKey> {
/// <summary>
+ /// How many association store requests should occur between each spring cleaning.
+ /// </summary>
+ private const int PeriodicCleaningFrequency = 10;
+
+ /// <summary>
/// For Relying Parties, this maps OP Endpoints to a set of associations with that endpoint.
/// For Providers, this keeps smart and dumb associations in two distinct pools.
/// </summary>
private Dictionary<TKey, Associations> serverAssocsTable = new Dictionary<TKey, Associations>();
/// <summary>
+ /// A counter to track how close we are to an expired association cleaning run.
+ /// </summary>
+ private int periodicCleaning;
+
+ /// <summary>
/// Stores a given association for later recall.
/// </summary>
/// <param name="distinguishingFactor">The distinguishing factor, either an OP Endpoint or smart/dumb mode.</param>
@@ -38,6 +48,13 @@ namespace DotNetOpenAuth.OpenId {
Associations server_assocs = this.serverAssocsTable[distinguishingFactor];
server_assocs.Set(association);
+
+ unchecked {
+ this.periodicCleaning++;
+ }
+ if (this.periodicCleaning % PeriodicCleaningFrequency == 0) {
+ this.ClearExpiredAssociations();
+ }
}
}
@@ -88,17 +105,6 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
- /// Clears all expired associations from the store.
- /// </summary>
- public void ClearExpiredAssociations() {
- lock (this) {
- foreach (Associations assocs in this.serverAssocsTable.Values) {
- assocs.ClearExpired();
- }
- }
- }
-
- /// <summary>
/// Gets the server associations for a given OP Endpoint or dumb/smart mode.
/// </summary>
/// <param name="distinguishingFactor">The distinguishing factor, either an OP Endpoint (for relying parties) or smart/dumb (for providers).</param>
@@ -112,5 +118,16 @@ namespace DotNetOpenAuth.OpenId {
return this.serverAssocsTable[distinguishingFactor];
}
}
+
+ /// <summary>
+ /// Clears all expired associations from the store.
+ /// </summary>
+ private void ClearExpiredAssociations() {
+ lock (this) {
+ foreach (Associations assocs in this.serverAssocsTable.Values) {
+ assocs.ClearExpired();
+ }
+ }
+ }
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs b/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs
index 9051998..01b74a1 100644
--- a/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs
+++ b/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs
@@ -115,12 +115,7 @@ namespace DotNetOpenAuth.OpenId.Behaviors {
bool IProviderBehavior.OnIncomingRequest(IRequest request) {
var extensionRequest = request as Provider.HostProcessedRequest;
if (extensionRequest != null) {
- if (extensionRequest.GetExtension<ClaimsRequest>() == null) {
- ClaimsRequest sreg = extensionRequest.UnifyExtensionsAsSreg();
- if (sreg != null) {
- ((IProtocolMessageWithExtensions)extensionRequest.RequestMessage).Extensions.Add(sreg);
- }
- }
+ extensionRequest.UnifyExtensionsAsSreg();
}
return false;
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs
index 338c480..1b58c2f 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs
@@ -130,7 +130,8 @@ namespace DotNetOpenAuth.OpenId.Extensions {
/// <summary>
/// Looks for Simple Registration and Attribute Exchange (all known formats)
- /// request extensions and returns them as a Simple Registration extension.
+ /// request extensions and returns them as a Simple Registration extension,
+ /// and adds the new extension to the original request message if it was absent.
/// </summary>
/// <param name="request">The authentication request.</param>
/// <returns>
@@ -140,7 +141,7 @@ namespace DotNetOpenAuth.OpenId.Extensions {
internal static ClaimsRequest UnifyExtensionsAsSreg(this Provider.IHostProcessedRequest request) {
Contract.Requires<ArgumentNullException>(request != null);
- var req = (Provider.AuthenticationRequest)request;
+ var req = (Provider.HostProcessedRequest)request;
var sreg = req.GetExtension<ClaimsRequest>();
if (sreg != null) {
return sreg;
@@ -148,7 +149,7 @@ namespace DotNetOpenAuth.OpenId.Extensions {
var ax = req.GetExtension<FetchRequest>();
if (ax != null) {
- sreg = new ClaimsRequest();
+ sreg = new ClaimsRequest(SimpleRegistration.Constants.sreg_ns);
sreg.Synthesized = true;
((IProtocolMessageWithExtensions)req.RequestMessage).Extensions.Add(sreg);
sreg.BirthDate = GetDemandLevelFor(ax, WellKnownAttributes.BirthDate.WholeBirthDate);
diff --git a/src/DotNetOpenAuth/OpenId/IAssociationStore.cs b/src/DotNetOpenAuth/OpenId/IAssociationStore.cs
index 71d8652..fb4487c 100644
--- a/src/DotNetOpenAuth/OpenId/IAssociationStore.cs
+++ b/src/DotNetOpenAuth/OpenId/IAssociationStore.cs
@@ -36,6 +36,12 @@ namespace DotNetOpenAuth.OpenId {
/// <see cref="System.Uri"/> for consumers (to distinguish associations across servers) or
/// <see cref="AssociationRelyingPartyType"/> for providers (to distinguish dumb and smart client associations).
/// </typeparam>
+ /// <remarks>
+ /// Expired associations should be periodically cleared out of an association store.
+ /// This should be done frequently enough to avoid a memory leak, but sparingly enough
+ /// to not be a performance drain. Because this balance can vary by host, it is the
+ /// responsibility of the host to initiate this cleaning.
+ /// </remarks>
////[ContractClass(typeof(IAssociationStoreContract<>))]
public interface IAssociationStore<TKey> {
/// <summary>
@@ -84,17 +90,6 @@ namespace DotNetOpenAuth.OpenId {
/// before this call.
/// </remarks>
bool RemoveAssociation(TKey distinguishingFactor, string handle);
-
- /// <summary>
- /// Clears all expired associations from the store.
- /// </summary>
- /// <remarks>
- /// If another algorithm is in place to periodically clear out expired associations,
- /// this method call may be ignored.
- /// This should be done frequently enough to avoid a memory leak, but sparingly enough
- /// to not be a performance drain.
- /// </remarks>
- void ClearExpiredAssociations();
}
// For some odd reason, having this next class causes our test project to fail to build with this error:
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/Provider/StandardProviderApplicationStore.cs b/src/DotNetOpenAuth/OpenId/Provider/StandardProviderApplicationStore.cs
index f33a655..4fa2d64 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/StandardProviderApplicationStore.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/StandardProviderApplicationStore.cs
@@ -100,19 +100,6 @@ namespace DotNetOpenAuth.OpenId.Provider {
return this.associationStore.RemoveAssociation(distinguishingFactor, handle);
}
- /// <summary>
- /// Clears all expired associations from the store.
- /// </summary>
- /// <remarks>
- /// If another algorithm is in place to periodically clear out expired associations,
- /// this method call may be ignored.
- /// This should be done frequently enough to avoid a memory leak, but sparingly enough
- /// to not be a performance drain.
- /// </remarks>
- public void ClearExpiredAssociations() {
- this.associationStore.ClearExpiredAssociations();
- }
-
#endregion
#region INonceStore Members
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/AssociationManager.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs
index 3e0a067..ac70387 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs
@@ -149,10 +149,20 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
return null;
}
- var associateRequest = AssociateRequest.Create(this.securitySettings, provider);
+ try {
+ var associateRequest = AssociateRequest.Create(this.securitySettings, provider);
- const int RenegotiateRetries = 1;
- return this.CreateNewAssociation(provider, associateRequest, RenegotiateRetries);
+ const int RenegotiateRetries = 1;
+ return this.CreateNewAssociation(provider, associateRequest, RenegotiateRetries);
+ } catch (VerificationException ex) {
+ // See Trac ticket #163. In partial trust host environments, the
+ // Diffie-Hellman implementation we're using for HTTP OP endpoints
+ // sometimes causes the CLR to throw:
+ // "VerificationException: Operation could destabilize the runtime."
+ // Just give up and use dumb mode in this case.
+ Logger.OpenId.ErrorFormat("VerificationException occurred while trying to create an association with {0}. {1}", provider.Uri, ex);
+ return null;
+ }
}
/// <summary>
@@ -223,14 +233,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
// the exception so that auth may continue in dumb mode.
Logger.OpenId.ErrorFormat("An error occurred while trying to create an association with {0}. {1}", provider.Uri, ex);
return null;
- } catch (VerificationException ex) {
- // See Trac ticket #163. In partial trust host environments, the
- // Diffie-Hellman implementation we're using for HTTP OP endpoints
- // sometimes causes the CLR to throw:
- // "VerificationException: Operation could destabilize the runtime."
- // Just give up and use dumb mode in this case.
- Logger.OpenId.ErrorFormat("VerificationException occurred while trying to create an association with {0}. {1}", provider.Uri, ex);
- return null;
}
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
index e90c1d3..09383bb 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
@@ -11,6 +11,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
+ using System.Threading;
using System.Web;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.ChannelElements;
@@ -297,6 +298,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <remarks>
/// This method requires an ASP.NET HttpContext.
/// </remarks>
+ /// <exception cref="ThreadAbortException">Typically thrown by ASP.NET in order to prevent additional data from the page being sent to the client and corrupting the response.</exception>
public void RedirectToProvider() {
this.RedirectingResponse.Send();
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs
index f0a1574..fdb6931 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs
@@ -84,19 +84,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
return this.associationStore.RemoveAssociation(distinguishingFactor, handle);
}
- /// <summary>
- /// Clears all expired associations from the store.
- /// </summary>
- /// <remarks>
- /// If another algorithm is in place to periodically clear out expired associations,
- /// this method call may be ignored.
- /// This should be done frequently enough to avoid a memory leak, but sparingly enough
- /// to not be a performance drain.
- /// </remarks>
- public void ClearExpiredAssociations() {
- this.associationStore.ClearExpiredAssociations();
- }
-
#endregion
#region INonceStore Members
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>
diff --git a/src/version.txt b/src/version.txt
index 6cb9d3d..f989260 100644
--- a/src/version.txt
+++ b/src/version.txt
@@ -1 +1 @@
-3.4.3
+3.4.4