diff options
Diffstat (limited to 'src')
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 |