diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2011-06-09 17:14:03 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2011-06-09 17:14:03 -0700 |
commit | a921328045f5711fa4136a1f57d5db4745117905 (patch) | |
tree | fe2d7cfa663bdd22d4a85f3b19f9a66636610763 /src | |
parent | 2751e08721af51437ae5738e2da23dd460df6cc8 (diff) | |
parent | d916633668329aa07f0b6f2ee952268a5dff8069 (diff) | |
download | DotNetOpenAuth-a921328045f5711fa4136a1f57d5db4745117905.zip DotNetOpenAuth-a921328045f5711fa4136a1f57d5db4745117905.tar.gz DotNetOpenAuth-a921328045f5711fa4136a1f57d5db4745117905.tar.bz2 |
Merge branch 'v3.4' into sample2legged
Diffstat (limited to 'src')
65 files changed, 1786 insertions, 541 deletions
diff --git a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj index 5e80f2c..2067ede 100644 --- a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj +++ b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj @@ -74,6 +74,7 @@ <CodeContractsEmitXMLDocs>False</CodeContractsEmitXMLDocs> <CodeContractsExtraRewriteOptions /> <CodeContractsReferenceAssembly>%28none%29</CodeContractsReferenceAssembly> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <DebugType>pdbonly</DebugType> @@ -111,6 +112,7 @@ <CodeContractsEmitXMLDocs>False</CodeContractsEmitXMLDocs> <CodeContractsExtraRewriteOptions /> <CodeContractsReferenceAssembly>%28none%29</CodeContractsReferenceAssembly> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'CodeAnalysis|AnyCPU' "> <DebugSymbols>true</DebugSymbols> @@ -150,6 +152,7 @@ <CodeContractsEmitXMLDocs>False</CodeContractsEmitXMLDocs> <CodeContractsExtraRewriteOptions /> <CodeContractsReferenceAssembly>%28none%29</CodeContractsReferenceAssembly> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup> <ItemGroup> <Reference Include="log4net" /> @@ -301,6 +304,9 @@ <Compile Include="OpenId\RelyingParty\IdentifierDiscoveryResultTests.cs" /> <Compile Include="OpenId\UriIdentifierTests.cs" /> <Compile Include="OpenId\XriIdentifierTests.cs" /> + <Compile Include="Performance\CodeTimers.cs" /> + <Compile Include="Performance\HighPerformance.cs" /> + <Compile Include="Performance\PerformanceTestUtilities.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Messaging\ResponseTests.cs" /> <Compile Include="OAuth\AppendixScenarios.cs" /> diff --git a/src/DotNetOpenAuth.Test/Logging.config b/src/DotNetOpenAuth.Test/Logging.config index 87da027..a32badd 100644 --- a/src/DotNetOpenAuth.Test/Logging.config +++ b/src/DotNetOpenAuth.Test/Logging.config @@ -17,6 +17,23 @@ <conversionPattern value="[%thread] %-5level - %message%newline" /> </layout> </appender> + <appender name="StdOutAppender" type="log4net.Appender.ConsoleAppender"> + <immediateFlush value="true" /> + <layout type="log4net.Layout.PatternLayout"> + <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" /> + </layout> + </appender> + <appender name="StdErrAppender" type="log4net.Appender.ConsoleAppender"> + <immediateFlush value="true" /> + <target value="Console.Error" /> + <filter type="log4net.Filter.LevelRangeFilter"> + <param name="LevelMin" value="ERROR"/> + <param name="LevelMax" value="FATAL"/> + </filter> + <layout type="log4net.Layout.PatternLayout"> + <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" /> + </layout> + </appender> <!-- Setup the root category, add the appenders and set the default level --> <root> <level value="Info" /> diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthChannel.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthChannel.cs index e862ca6..74e23bd 100644 --- a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthChannel.cs +++ b/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthChannel.cs @@ -24,29 +24,29 @@ namespace DotNetOpenAuth.Test.Mocks { /// <summary> /// Initializes a new instance of the <see cref="CoordinatingOAuthChannel"/> class for Consumers. /// </summary> - /// <param name="signingBindingElement"> - /// The signing element for the Consumer to use. Null for the Service Provider. - /// </param> + /// <param name="signingBindingElement">The signing element for the Consumer to use. Null for the Service Provider.</param> /// <param name="tokenManager">The token manager to use.</param> - internal CoordinatingOAuthChannel(ITamperProtectionChannelBindingElement signingBindingElement, IConsumerTokenManager tokenManager) + /// <param name="securitySettings">The security settings.</param> + internal CoordinatingOAuthChannel(ITamperProtectionChannelBindingElement signingBindingElement, IConsumerTokenManager tokenManager, DotNetOpenAuth.OAuth.ConsumerSecuritySettings securitySettings) : base( signingBindingElement, new NonceMemoryStore(StandardExpirationBindingElement.MaximumMessageAge), - tokenManager) { + tokenManager, + securitySettings) { } /// <summary> /// Initializes a new instance of the <see cref="CoordinatingOAuthChannel"/> class for Consumers. /// </summary> - /// <param name="signingBindingElement"> - /// The signing element for the Consumer to use. Null for the Service Provider. - /// </param> + /// <param name="signingBindingElement">The signing element for the Consumer to use. Null for the Service Provider.</param> /// <param name="tokenManager">The token manager to use.</param> - internal CoordinatingOAuthChannel(ITamperProtectionChannelBindingElement signingBindingElement, IServiceProviderTokenManager tokenManager) + /// <param name="securitySettings">The security settings.</param> + internal CoordinatingOAuthChannel(ITamperProtectionChannelBindingElement signingBindingElement, IServiceProviderTokenManager tokenManager, DotNetOpenAuth.OAuth.ServiceProviderSecuritySettings securitySettings) : base( signingBindingElement, new NonceMemoryStore(StandardExpirationBindingElement.MaximumMessageAge), - tokenManager) { + tokenManager, + securitySettings) { } /// <summary> diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs index 479375a..34cc3a4 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs @@ -27,6 +27,8 @@ namespace DotNetOpenAuth.Test.OAuth.ChannelElements { private TestWebRequestHandler webRequestHandler; private SigningBindingElementBase signingElement; private INonceStore nonceStore; + private DotNetOpenAuth.OAuth.ServiceProviderSecuritySettings serviceProviderSecuritySettings = DotNetOpenAuth.Configuration.DotNetOpenAuthSection.Configuration.OAuth.ServiceProvider.SecuritySettings.CreateSecuritySettings(); + private DotNetOpenAuth.OAuth.ConsumerSecuritySettings consumerSecuritySettings = DotNetOpenAuth.Configuration.DotNetOpenAuthSection.Configuration.OAuth.Consumer.SecuritySettings.CreateSecuritySettings(); [SetUp] public override void SetUp() { @@ -35,33 +37,33 @@ namespace DotNetOpenAuth.Test.OAuth.ChannelElements { this.webRequestHandler = new TestWebRequestHandler(); this.signingElement = new RsaSha1SigningBindingElement(new InMemoryTokenManager()); this.nonceStore = new NonceMemoryStore(StandardExpirationBindingElement.MaximumMessageAge); - this.channel = new OAuthChannel(this.signingElement, this.nonceStore, new InMemoryTokenManager(), new TestMessageFactory()); + this.channel = new OAuthChannel(this.signingElement, this.nonceStore, new InMemoryTokenManager(), this.serviceProviderSecuritySettings, new TestMessageFactory()); this.channel.WebRequestHandler = this.webRequestHandler; } [TestCase, ExpectedException(typeof(ArgumentNullException))] public void CtorNullSigner() { - new OAuthChannel(null, this.nonceStore, new InMemoryTokenManager(), new TestMessageFactory()); + new OAuthChannel(null, this.nonceStore, new InMemoryTokenManager(), this.consumerSecuritySettings, new TestMessageFactory()); } [TestCase, ExpectedException(typeof(ArgumentNullException))] public void CtorNullStore() { - new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), null, new InMemoryTokenManager(), new TestMessageFactory()); + new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), null, new InMemoryTokenManager(), this.consumerSecuritySettings, new TestMessageFactory()); } [TestCase, ExpectedException(typeof(ArgumentNullException))] public void CtorNullTokenManager() { - new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), this.nonceStore, null, new TestMessageFactory()); + new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), this.nonceStore, null, this.consumerSecuritySettings, new TestMessageFactory()); } [TestCase] public void CtorSimpleConsumer() { - new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), this.nonceStore, (IConsumerTokenManager)new InMemoryTokenManager()); + new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), this.nonceStore, (IConsumerTokenManager)new InMemoryTokenManager(), this.consumerSecuritySettings); } [TestCase] public void CtorSimpleServiceProvider() { - new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), this.nonceStore, (IServiceProviderTokenManager)new InMemoryTokenManager()); + new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), this.nonceStore, (IServiceProviderTokenManager)new InMemoryTokenManager(), this.serviceProviderSecuritySettings); } [TestCase] diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs index 2ef7e9a..83d5feb 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs @@ -5,6 +5,8 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.Test.OAuth.ChannelElements { + using System.Collections.Generic; + using System.Diagnostics.Contracts; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OAuth; @@ -52,6 +54,15 @@ namespace DotNetOpenAuth.Test.OAuth.ChannelElements { "GET&https%3A%2F%2Fwww.google.com%2Faccounts%2FOAuthGetRequestToken&oauth_consumer_key%3Dnerdbank.org%26oauth_nonce%3Dfe4045a3f0efdd1e019fa8f8ae3f5c38%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1222665749%26oauth_version%3D1.0%26scope%3Dhttp%253A%252F%252Fwww.google.com%252Fm8%252Ffeeds%252F", SigningBindingElementBase.ConstructSignatureBaseString(message, this.MessageDescriptions.GetAccessor(message))); + // Test for when oauth_version isn't explicitly included in the message by the consumer. + message = CreateTestRequestTokenMessageNoOAuthVersion( + this.MessageDescriptions, + new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken?scope=http://www.google.com/m8/feeds/", HttpDeliveryMethods.GetRequest)); + message.ExtraData.Remove("scope"); // remove it from ExtraData since we put it in the URL + Assert.AreEqual( + "GET&https%3A%2F%2Fwww.google.com%2Faccounts%2FOAuthGetRequestToken&oauth_consumer_key%3Dnerdbank.org%26oauth_nonce%3Dfe4045a3f0efdd1e019fa8f8ae3f5c38%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1222665749%26scope%3Dhttp%253A%252F%252Fwww.google.com%252Fm8%252Ffeeds%252F", + SigningBindingElementBase.ConstructSignatureBaseString(message, this.MessageDescriptions.GetAccessor(message))); + // This is a simulation of receiving the message, where the query string is still in the URL, // but has been read into ExtraData, so parameters in the query string appear twice. message = CreateTestRequestTokenMessage( @@ -62,6 +73,42 @@ namespace DotNetOpenAuth.Test.OAuth.ChannelElements { SigningBindingElementBase.ConstructSignatureBaseString(message, this.MessageDescriptions.GetAccessor(message))); } + [TestCase] + public void BaseSignatureStringResourceRequests() { + var message = this.CreateResourceRequest(new MessageReceivingEndpoint("http://tom.test.wishpot.com/restapi/List/Search?List.LastName=ciccotosto", HttpDeliveryMethods.GetRequest)); + message.ConsumerKey = "public"; + message.AccessToken = "tokenpublic"; + + var signedMessage = (ITamperResistantOAuthMessage)message; + signedMessage.HttpMethod = "GET"; + signedMessage.SignatureMethod = "HMAC-SHA1"; + + MessageDictionary dictionary = this.MessageDescriptions.GetAccessor(message); + dictionary["oauth_timestamp"] = "1302716502"; + dictionary["oauth_nonce"] = "2U5YsZvL"; + + Assert.AreEqual( + "GET&http%3A%2F%2Ftom.test.wishpot.com%2Frestapi%2FList%2FSearch&List.LastName%3Dciccotosto%26oauth_consumer_key%3Dpublic%26oauth_nonce%3D2U5YsZvL%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1302716502%26oauth_token%3Dtokenpublic%26oauth_version%3D1.0", + SigningBindingElementBase.ConstructSignatureBaseString(message, this.MessageDescriptions.GetAccessor(message))); + } + + internal static UnauthorizedTokenRequest CreateTestRequestTokenMessageNoOAuthVersion(MessageDescriptionCollection messageDescriptions, MessageReceivingEndpoint endpoint) { + endpoint = endpoint ?? new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest); + var parts = new Dictionary<string, string>(); + parts["oauth_consumer_key"] = "nerdbank.org"; + parts["oauth_timestamp"] = "1222665749"; + parts["oauth_nonce"] = "fe4045a3f0efdd1e019fa8f8ae3f5c38"; + parts["scope"] = "http://www.google.com/m8/feeds/"; + parts["oauth_signature_method"] = "HMAC-SHA1"; + parts["oauth_signature"] = "anything non-empty"; + + UnauthorizedTokenRequest message = new UnauthorizedTokenRequest(endpoint, Protocol.V10.Version); + MessageDictionary dictionary = messageDescriptions.GetAccessor(message); + MessageSerializer.Get(typeof(UnauthorizedTokenRequest)).Deserialize(parts, dictionary); + + return message; + } + internal static UnauthorizedTokenRequest CreateTestRequestTokenMessage(MessageDescriptionCollection messageDescriptions, MessageReceivingEndpoint endpoint) { endpoint = endpoint ?? new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest); UnauthorizedTokenRequest message = new UnauthorizedTokenRequest(endpoint, Protocol.V10.Version); @@ -76,5 +123,11 @@ namespace DotNetOpenAuth.Test.OAuth.ChannelElements { dictionary["scope"] = "http://www.google.com/m8/feeds/"; return message; } + + internal AccessProtectedResourceRequest CreateResourceRequest(MessageReceivingEndpoint endpoint) { + Contract.Requires(endpoint != null); + var message = new AccessProtectedResourceRequest(endpoint, Protocol.V10.Version); + return message; + } } } diff --git a/src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs b/src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs index 972dd2a..6bcc583 100644 --- a/src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs +++ b/src/DotNetOpenAuth.Test/OAuth/OAuthCoordinator.cs @@ -19,6 +19,8 @@ namespace DotNetOpenAuth.Test.OAuth { internal class OAuthCoordinator : CoordinatorBase<WebConsumer, ServiceProvider> { private ConsumerDescription consumerDescription; private ServiceProviderDescription serviceDescription; + private DotNetOpenAuth.OAuth.ServiceProviderSecuritySettings serviceProviderSecuritySettings = DotNetOpenAuth.Configuration.DotNetOpenAuthSection.Configuration.OAuth.ServiceProvider.SecuritySettings.CreateSecuritySettings(); + private DotNetOpenAuth.OAuth.ConsumerSecuritySettings consumerSecuritySettings = DotNetOpenAuth.Configuration.DotNetOpenAuthSection.Configuration.OAuth.Consumer.SecuritySettings.CreateSecuritySettings(); /// <summary>Initializes a new instance of the <see cref="OAuthCoordinator"/> class.</summary> /// <param name="consumerDescription">The description of the consumer.</param> @@ -50,8 +52,8 @@ namespace DotNetOpenAuth.Test.OAuth { serviceTokenManager.AddConsumer(this.consumerDescription); // Prepare channels that will pass messages directly back and forth. - CoordinatingOAuthChannel consumerChannel = new CoordinatingOAuthChannel(consumerSigningElement, (IConsumerTokenManager)consumerTokenManager); - CoordinatingOAuthChannel serviceProviderChannel = new CoordinatingOAuthChannel(spSigningElement, (IServiceProviderTokenManager)serviceTokenManager); + CoordinatingOAuthChannel consumerChannel = new CoordinatingOAuthChannel(consumerSigningElement, (IConsumerTokenManager)consumerTokenManager, this.consumerSecuritySettings); + CoordinatingOAuthChannel serviceProviderChannel = new CoordinatingOAuthChannel(spSigningElement, (IServiceProviderTokenManager)serviceTokenManager, this.serviceProviderSecuritySettings); consumerChannel.RemoteChannel = serviceProviderChannel; serviceProviderChannel.RemoteChannel = consumerChannel; diff --git a/src/DotNetOpenAuth.Test/OpenId/Provider/PerformanceTests.cs b/src/DotNetOpenAuth.Test/OpenId/Provider/PerformanceTests.cs index 7984b58..4530982 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Provider/PerformanceTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Provider/PerformanceTests.cs @@ -18,6 +18,7 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { using DotNetOpenAuth.OpenId.ChannelElements; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.Provider; + using DotNetOpenAuth.Test.Performance; using NUnit.Framework; [TestFixture, Category("Performance")] @@ -29,73 +30,50 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { [SetUp] public override void SetUp() { base.SetUp(); - SuspendLogging(); this.provider = CreateProvider(); } - [TearDown] - public override void Cleanup() { - ResumeLogging(); - base.Cleanup(); - } - [TestCase] public void AssociateDH() { var associateRequest = this.CreateAssociateRequest(OPUri); - Stopwatch timer = new Stopwatch(); - timer.Start(); - int iterations; - for (iterations = 0; timer.ElapsedMilliseconds < TestRunTime.TotalMilliseconds; iterations++) { - IRequest request = this.provider.GetRequest(associateRequest); - var response = this.provider.PrepareResponse(request); - Assert.IsInstanceOf<AssociateSuccessfulResponse>(response.OriginalMessage); - } - timer.Stop(); - double executionsPerSecond = GetExecutionsPerSecond(iterations, timer); - TestUtilities.TestLogger.InfoFormat("Created {0} associations in {1}, or {2} per second.", iterations, timer.Elapsed, executionsPerSecond); - Assert.IsTrue(executionsPerSecond >= 2, "Too slow ({0} >= 2 executions per second required.)", executionsPerSecond); + MeasurePerformance( + () => { + IRequest request = this.provider.GetRequest(associateRequest); + var response = this.provider.PrepareResponse(request); + Assert.IsInstanceOf<AssociateSuccessfulResponse>(response.OriginalMessage); + }, + maximumAllowedUnitTime: 3.5e6f, + iterations: 1); } [TestCase] public void AssociateClearText() { var associateRequest = this.CreateAssociateRequest(OPUriSsl); // SSL will cause a plaintext association - Stopwatch timer = new Stopwatch(); - timer.Start(); - int iterations; - for (iterations = 0; timer.ElapsedMilliseconds < TestRunTime.TotalMilliseconds; iterations++) { - IRequest request = this.provider.GetRequest(associateRequest); - var response = this.provider.PrepareResponse(request); - Assert.IsInstanceOf<AssociateSuccessfulResponse>(response.OriginalMessage); - } - timer.Stop(); - double executionsPerSecond = GetExecutionsPerSecond(iterations, timer); - TestUtilities.TestLogger.InfoFormat("Created {0} associations in {1}, or {2} per second.", iterations, timer.Elapsed, executionsPerSecond); - Assert.IsTrue(executionsPerSecond > 1000, "Too slow ({0} > 1000 executions per second required.)", executionsPerSecond); + MeasurePerformance( + () => { + IRequest request = this.provider.GetRequest(associateRequest); + var response = this.provider.PrepareResponse(request); + Assert.IsInstanceOf<AssociateSuccessfulResponse>(response.OriginalMessage); + }, + maximumAllowedUnitTime: 1.5e4f, + iterations: 1000); } [TestCase] public void CheckIdSharedHmacSha1Association() { Protocol protocol = Protocol.Default; string assocType = protocol.Args.SignatureAlgorithm.HMAC_SHA1; - double executionsPerSecond = this.ParameterizedCheckIdTest(protocol, assocType); - TestUtilities.TestLogger.InfoFormat("{0} executions per second.", executionsPerSecond); - Assert.IsTrue(executionsPerSecond > 500, "Too slow ({0} > 500 executions per second required.)", executionsPerSecond); + this.ParameterizedCheckIdTest(protocol, assocType); } [TestCase] public void CheckIdSharedHmacSha256Association() { Protocol protocol = Protocol.Default; string assocType = protocol.Args.SignatureAlgorithm.HMAC_SHA256; - double executionsPerSecond = this.ParameterizedCheckIdTest(protocol, assocType); - TestUtilities.TestLogger.InfoFormat("{0} executions per second.", executionsPerSecond); - Assert.IsTrue(executionsPerSecond > 400, "Too slow ({0} > 400 executions per second required.)", executionsPerSecond); + this.ParameterizedCheckIdTest(protocol, assocType); } - private static double GetExecutionsPerSecond(int iterations, Stopwatch timer) { - return (double)iterations / (timer.ElapsedMilliseconds / 1000); - } - - private double ParameterizedCheckIdTest(Protocol protocol, string assocType) { + private void ParameterizedCheckIdTest(Protocol protocol, string assocType) { Association assoc = HmacShaAssociation.Create( protocol, assocType, @@ -103,19 +81,14 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { this.provider.SecuritySettings); this.provider.AssociationStore.StoreAssociation(AssociationRelyingPartyType.Smart, assoc); var checkidRequest = this.CreateCheckIdRequest(true); - Stopwatch timer = new Stopwatch(); - timer.Start(); - int iterations; - for (iterations = 0; timer.ElapsedMilliseconds < TestRunTime.TotalMilliseconds; iterations++) { - var request = (IAuthenticationRequest)this.provider.GetRequest(checkidRequest); - request.IsAuthenticated = true; - var response = this.provider.PrepareResponse(request); - Assert.IsInstanceOf<PositiveAssertionResponse>(response.OriginalMessage); - } - timer.Stop(); - double executionsPerSecond = GetExecutionsPerSecond(iterations, timer); - TestUtilities.TestLogger.InfoFormat("Responded to {0} checkid messages in {1}; or {2} authentications per second.", iterations, timer.Elapsed, executionsPerSecond); - return executionsPerSecond; + MeasurePerformance( + () => { + var request = (IAuthenticationRequest)this.provider.GetRequest(checkidRequest); + request.IsAuthenticated = true; + var response = this.provider.PrepareResponse(request); + Assert.IsInstanceOf<PositiveAssertionResponse>(response.OriginalMessage); + }, + maximumAllowedUnitTime: 6.8e4f); } private HttpRequestInfo CreateAssociateRequest(Uri opEndpoint) { diff --git a/src/DotNetOpenAuth.Test/Performance/CodeTimers.cs b/src/DotNetOpenAuth.Test/Performance/CodeTimers.cs new file mode 100644 index 0000000..85fa83d --- /dev/null +++ b/src/DotNetOpenAuth.Test/Performance/CodeTimers.cs @@ -0,0 +1,401 @@ +// <auto-generated/> // well, imported. But this gets StyleCop off our back +//----------------------------------------------------------------------- +// <copyright file="CodeTimers.cs" company="Microsoft Corporation"> +// Copyright (c) Microsoft Corporation. All rights reserved. +// </copyright> +// <author>Vance Morrison</author> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Test.Performance { + using System; + using System.Collections.Generic; + using System.Diagnostics; + + /// <summary> + /// Stats represents a list of samples (floating point values) This class can calculate the standard + /// statistics on this list (Mean, Median, StandardDeviation ...) + /// </summary> + internal class Stats : IEnumerable<float> { + private List<float> data; + private float minimum; + private float maximum; + private float median; + private float mean; + private float standardDeviation; + private bool statsComputed; + + public Stats() { data = new List<float>(); } + + public void Add(float dataItem) { + statsComputed = false; + data.Add(dataItem); + } + public void RemoveRange(int index, int count) { + data.RemoveRange(index, count); + statsComputed = false; + } + internal void Adjust(float delta) { + statsComputed = false; + for (int i = 0; i < data.Count; i++) + data[i] += delta; + } + internal void AdjustForScale(float scale) { + statsComputed = false; + for (int i = 0; i < data.Count; i++) + data[i] /= scale; + } + public int Count { get { return data.Count; } } + public float this[int idx] { get { return data[idx]; } } + public IEnumerator<float> GetEnumerator() { return data.GetEnumerator(); } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return data.GetEnumerator(); } + + public float Minimum { + get { + if (!statsComputed) { + this.ComputeStats(); + } + + return minimum; + } + } + public float Maximum { + get { + if (!statsComputed) { + this.ComputeStats(); + } + + return maximum; + } + } + public float Median { + get { + if (!statsComputed) { + this.ComputeStats(); + } + + return median; + } + } + + public float Mean { + get { + if (!statsComputed) { + this.ComputeStats(); + } + + return mean; + } + } + + public float StandardDeviation { + get { + if (!statsComputed) { + this.ComputeStats(); + } + + return standardDeviation; + } + } + + /// <summary> + /// Returns a <see cref="System.String"/> that represents this instance. + /// </summary> + /// <returns> + /// A <see cref="System.String"/> that represents this instance. + /// </returns> + public override string ToString() { + if (!statsComputed) { + ComputeStats(); + } + + return "mean=" + mean.ToString("f3") + " median=" + median.ToString("f3") + + " min=" + minimum.ToString("f3") + " max=" + maximum.ToString("f3") + + " sdtdev=" + standardDeviation.ToString("f3") + " samples=" + Count.ToString(); + } + + private void ComputeStats() { + minimum = float.MaxValue; + maximum = float.MinValue; + median = 0.0F; + mean = 0.0F; + standardDeviation = 0.0F; + + double total = 0; + foreach (float dataPoint in this) { + if (dataPoint < minimum) + minimum = dataPoint; + if (dataPoint > maximum) + maximum = dataPoint; + total += dataPoint; + } + + if (Count > 0) { + data.Sort(); + if (Count % 2 == 1) + median = this[Count / 2]; + else + median = (this[(Count / 2) - 1] + this[Count / 2]) / 2; + mean = (float)(total / Count); + + double squares = 0.0; + foreach (float dataPoint in this) { + double diffFromMean = dataPoint - mean; + squares += diffFromMean * diffFromMean; + } + standardDeviation = (float)Math.Sqrt(squares / Count); + } + + statsComputed = true; + } + }; + + /// <summary> + /// The CodeTimer class only times one invocation of the code. Often, you want to collect many samples so + /// that you can determine how noisy the resulting data is. This is what MultiSampleCodeTimer does. + /// </summary> + internal class MultiSampleCodeTimer { + public MultiSampleCodeTimer() : this(1) { } + public MultiSampleCodeTimer(int sampleCount) : this(sampleCount, 1) { } + public MultiSampleCodeTimer(int sampleCount, int iterationCount) { + SampleCount = sampleCount; + timer = new CodeTimer(iterationCount); + timer.Prime = false; // We will do the priming (or not). + Prime = true; + } + public MultiSampleCodeTimer(MultiSampleCodeTimer template) + : this(template.SampleCount, template.IterationCount) { + OnMeasure = template.OnMeasure; + } + + /// <summary> + /// If true (the default), the benchmark is run once before the actual measurement to + /// insure that any 'first time' initialization is complete. + /// </summary> + public bool Prime; + /// <summary> + /// The number of times the benchmark is run in a loop for a single measument. + /// </summary> + public int IterationCount { get { return timer.IterationCount; } set { timer.IterationCount = value; } } + /// <summary> + /// The number of measurments to make for a single benchmark. + /// </summary> + public int SampleCount; + /// <summary> + /// The smallest time (in microseconds) that can be resolved by the timer). + /// </summary> + public static float ResolutionUsec { get { return 1000000.0F / Stopwatch.Frequency; } } + + public delegate void MeasureCallback(string name, int iterationCount, float scale, Stats sample); + /// <summary> + /// OnMeasure is signaled every time a Measure() is called. + /// </summary> + public event MeasureCallback OnMeasure; + + public Stats Measure(string name, Action action) { + return Measure(name, 1, action, null); + } + /// <summary> + /// The main measurment routine. Calling this will cause code:OnMeasure event to be + /// raised. + /// </summary> + /// <param name="name">name of the benchmark</param> + /// <param name="scale">The number of times the benchmark is cloned in 'action' (typically 1)</param> + /// <param name="action">The actual code to measure.</param> + /// <returns>A Stats object representing the measurements (in usec)</returns> + public Stats Measure(string name, float scale, Action action) { + return Measure(name, scale, action, null); + } + /// <summary> + /// The main measurment routine. Calling this will cause code:OnMeasure event to be + /// raised. + /// </summary> + /// <param name="name">name of the benchmark</param> + /// <param name="scale">The number of times the benchmark is cloned in 'action' (typically 1)</param> + /// <param name="action">The actual code to measure.</param> + /// <param name="reset">Code that will be called before 'action' to reset the state of the benchmark.</param> + /// <returns>A Stats object representing the measurements (in usec)</returns> + public Stats Measure(string name, float scale, Action action, Action reset) { + if (reset != null && IterationCount != 1) + throw new ApplicationException("Reset can only be used on timers with an iteration count of 1"); + Stats statsUSec = new Stats(); + if (Prime) { + if (reset != null) + reset(); + action(); + } + for (int i = 0; i < SampleCount; i++) { + if (reset != null) + reset(); + statsUSec.Add(timer.Measure(name, scale, action)); + } + + if (OnMeasure != null) + OnMeasure(name, IterationCount, scale, statsUSec); + return statsUSec; + } + + /// <summary> + /// Prints the mean, median, min, max, and stdDev and count of the samples to the Console + /// Useful as a target for OnMeasure + /// </summary> + public static MeasureCallback PrintStats = delegate(string name, int iterationCount, float scale, Stats sample) { + Console.WriteLine(name + ": " + sample.ToString()); + }; + /// <summary> + /// Prints the mean with a error bound (2 standard deviations, which imply a you have + /// 95% confidence that a sampleUsec will be with the bounds (for a normal distribution). + /// This is a good default target for OnMeasure. + /// </summary> + public static MeasureCallback Print = delegate(string name, int iterationCount, float scale, Stats sample) { + // +- two standard deviations covers 95% of all samples in a normal distribution + float errorPercent = (sample.StandardDeviation * 2 * 100) / Math.Abs(sample.Mean); + string errorString = ">400%"; + if (errorPercent < 400) + errorString = (errorPercent.ToString("f0") + "%").PadRight(5); + string countString = ""; + if (iterationCount != 1) + countString = "count: " + iterationCount.ToString() + " "; + Console.WriteLine(name + ": " + countString + sample.Mean.ToString("f3").PadLeft(8) + " +- " + errorString + " msec"); + }; + + #region privates + CodeTimer timer; + #endregion + }; + + /// <summary> + /// CodeTimer is a simple wrapper that uses System.Diagnostics.StopWatch + /// to time the body of some code (given by a delegate), to high precision. + /// </summary> + public class CodeTimer { + public CodeTimer() : this(1) { } + public CodeTimer(int iterationCount) { + this.iterationCount = iterationCount; + Prime = true; + + // Spin the CPU for a while. This should help insure that the CPU gets out of any low power + // mode so so that we get more stable results. + // TODO: see if this is true, and if there is a better way of doing it. + Stopwatch sw = Stopwatch.StartNew(); + while (sw.ElapsedMilliseconds < 32) { + } + } + /// <summary> + /// The number of times the benchmark is run in a loop for a single measument. + /// </summary> + public int IterationCount { + get { return iterationCount; } + set { + iterationCount = value; + overheadValid = false; + } + } + /// <summary> + /// By default CodeTimer will run the action once before doing a + /// measurement run. This insures one-time actions like JIT + /// compilation are not being measured. However if the benchmark is + /// not idempotent, this can be a problem. Setting Prime=false + /// insures that this Priming does not happen. + /// </summary> + public bool Prime; + public delegate void MeasureCallback(string name, int iterationCount, float sample); + /// <summary> + /// OnMeasure is signaled every time a Measure() is called. + /// </summary> + public event MeasureCallback OnMeasure; + /// <summary> + /// The smallest time (in microseconds) that can be resolved by the timer). + /// </summary> + public static float ResolutionUsec { get { return 1000000.0F / Stopwatch.Frequency; } } + /// <summary> + /// Returns the number of microsecond it took to run 'action', 'count' times. + /// </summary> + public float Measure(string name, Action action) { + return Measure(name, 1, action); + } + /// <summary> + /// Returns the number of microseconds it to to run action 'count' times divided by 'scale'. + /// Scaling is useful if you want to normalize to a single iteration for example. + /// </summary> + public float Measure(string name, float scale, Action action) { + Stopwatch sw = new Stopwatch(); + + // Run the action once to do any JITTing that might happen. + if (Prime) + action(); + float overheadUsec = GetOverheadUsec(action); + + sw.Reset(); + sw.Start(); + for (int j = 0; j < iterationCount; j++) + action(); + sw.Stop(); + + float sampleUsec = (float)((sw.Elapsed.TotalMilliseconds * 1000.0F - overheadUsec) / scale / iterationCount); + if (!computingOverhead && OnMeasure != null) + OnMeasure(name, iterationCount, sampleUsec); + return sampleUsec; + } + /// <summary> + /// Prints the result of a CodeTimer to standard output. + /// This is a good default target for OnMeasure. + /// </summary> + public static MeasureCallback Print = delegate(string name, int iterationCount, float sample) { + Console.WriteLine("{0}: count={1} time={2:f3} msec ", name, iterationCount, sample); + }; + #region privates + + /// <summary> + /// Time the overheadUsec of the harness that does nothing so we can subtract it out. + /// + /// Because calling delegates on static methods is more expensive than caling delegates on + /// instance methods we need the action to determine the overheadUsec. + /// </summary> + /// <returns></returns> + float GetOverheadUsec(Action action) { + if (!overheadValid) { + if (computingOverhead) + return 0.0F; + computingOverhead = true; + + // Compute the overheads of calling differnet types of delegates. + Action emptyInstanceAction = new Action(this.emptyMethod); + // Prime the actions (JIT them) + Measure(null, emptyInstanceAction); + // Compute the min over 5 runs (figuring better not to go negative) + instanceOverheadUsec = float.MaxValue; + for (int i = 0; i < 5; i++) { + // We multiply by iteration count because we don't want this scaled by the + // count but 'Measure' does it by whether we want it or not. + instanceOverheadUsec = Math.Min(Measure(null, emptyInstanceAction) * IterationCount, instanceOverheadUsec); + } + + Action emptyStaticAction = new Action(emptyStaticMethod); + Measure(null, emptyStaticAction); + staticOverheadUsec = float.MaxValue; + for (int i = 0; i < 5; i++) + staticOverheadUsec = Math.Min(Measure(null, emptyStaticAction) * IterationCount, staticOverheadUsec); + + computingOverhead = false; + overheadValid = true; + } + + if (action.Target == null) + return staticOverheadUsec; + else + return instanceOverheadUsec; + } + + static private void emptyStaticMethod() { } + private void emptyMethod() { } + + bool overheadValid; + bool computingOverhead; + int iterationCount; + float staticOverheadUsec; + float instanceOverheadUsec; + + #endregion + }; +} + diff --git a/src/DotNetOpenAuth.Test/Performance/HighPerformance.cs b/src/DotNetOpenAuth.Test/Performance/HighPerformance.cs new file mode 100644 index 0000000..695aeda --- /dev/null +++ b/src/DotNetOpenAuth.Test/Performance/HighPerformance.cs @@ -0,0 +1,178 @@ +//----------------------------------------------------------------------- +// <copyright file="HighPerformance.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Test.Performance { + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.Runtime.InteropServices; + using System.Threading; + using log4net; + using NUnit.Framework; + + /// <summary> + /// Suppresses logging and forces the CPU into a high performance mode. + /// </summary> + internal class HighPerformance : IDisposable { + private readonly log4net.Core.Level originalLoggerThreshold; + private readonly PowerManagment.PowerSetting powerSetting; + private readonly ProcessPriorityClass originalProcessPriority; + +#pragma warning disable 0618 + /// <summary> + /// Initializes a new instance of the <see cref="HighPerformance"/> class. + /// </summary> + internal HighPerformance() { + ////if (!WaitForQuietCpu()) { + //// Assert.Inconclusive("Timed out waiting for a quiet CPU in which to perform perf tests."); + ////} + + this.originalLoggerThreshold = LogManager.GetLoggerRepository().Threshold; + LogManager.GetLoggerRepository().Threshold = LogManager.GetLoggerRepository().LevelMap["OFF"]; + this.powerSetting = new PowerManagment.PowerSetting(PowerManagment.PowerProfiles.HighPerformance); + this.originalProcessPriority = Process.GetCurrentProcess().PriorityClass; + Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High; + Thread.CurrentThread.Priority = ThreadPriority.Highest; + SpinCpu(); + } + + /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> + public void Dispose() { + Thread.CurrentThread.Priority = ThreadPriority.Normal; + Process.GetCurrentProcess().PriorityClass = this.originalProcessPriority; + this.powerSetting.Dispose(); // restores original power setting. + LogManager.GetLoggerRepository().Threshold = this.originalLoggerThreshold; + } +#pragma warning restore 0618 + + /// <summary> + /// Runs the CPU in a tight loop to get it out of any low power state. + /// </summary> + private static void SpinCpu() { + int dummy; + new MultiSampleCodeTimer(10, 1000).Measure( + "Loop 1K times", + 1, + delegate { + int k = 0; + while (k < 1000) { + k++; // still in danger of being optimized. + } + + dummy = k; // avoid optimization. + }); + } + + private static bool WaitForQuietCpu(float maxCpuSpike = 25, int minSecondsOfQuiet = 2, int maxSecondsBeforeGiveUp = 30) { + using (var pc = new System.Diagnostics.PerformanceCounter()) { + pc.CategoryName = "Processor"; + pc.CounterName = "% Processor Time"; + pc.InstanceName = "_Total"; + + TimeSpan samplingInterval = TimeSpan.FromMilliseconds(1000); + TimeSpan minimumQuietTime = TimeSpan.FromSeconds(minSecondsOfQuiet); + TimeSpan maximumTimeBeforeBail = TimeSpan.FromSeconds(maxSecondsBeforeGiveUp); + DateTime startTryingStamp = DateTime.Now; + int hitsRequired = (int)(minimumQuietTime.TotalMilliseconds / samplingInterval.TotalMilliseconds); + while (DateTime.Now - startTryingStamp < maximumTimeBeforeBail) { + int hits; + for (hits = 0; hits < hitsRequired; hits++) { + float currentCpuUtilization = pc.NextValue(); + if (currentCpuUtilization > maxCpuSpike) { + ////Console.WriteLine("Miss: CPU at {0}% utilization", currentCpuUtilization); + break; + } + + ////Console.WriteLine("Hit: CPU at {0}% utilization", currentCpuUtilization); + Thread.Sleep(samplingInterval); + } + + if (hits == hitsRequired) { + return true; + } + + Thread.Sleep(samplingInterval); + } + + return false; + } + } + + /// <summary> + /// PowerManagement allows you to access the funtionality of the Control Panel -> Power Options + /// dialog in windows. (Currently we only use VISTA APIs). + /// </summary> + private static class PowerManagment { + internal static unsafe Guid CurrentPolicy { + get { + Guid* retPolicy = null; + Guid ret = Guid.Empty; + try { + int callRet = PowerGetActiveScheme(IntPtr.Zero, ref retPolicy); + if (callRet == 0) { + ret = *retPolicy; + Marshal.FreeHGlobal((IntPtr)retPolicy); + } + } catch (Exception) { + } + return ret; + } + + set { + Guid newPolicy = value; + int result = PowerSetActiveScheme(IntPtr.Zero, ref newPolicy); + if (result != 0) { + TestUtilities.TestLogger.ErrorFormat("Unable to set power management policy. Error code: {0}", result); + ////throw new Win32Exception(result); + } + } + } + + [DllImport("powrprof.dll")] + private static unsafe extern int PowerGetActiveScheme(IntPtr reservedZero, ref Guid* policyGuidRet); + + [DllImport("powrprof.dll")] + private static extern int PowerSetActiveScheme(IntPtr reservedZero, ref Guid policyGuid); + + internal static class PowerProfiles { + internal static Guid HighPerformance = new Guid(0x8c5e7fda, 0xe8bf, 0x4a96, 0x9a, 0x85, 0xa6, 0xe2, 0x3a, 0x8c, 0x63, 0x5c); + + internal static Guid Balanced = new Guid(0x381b4222, 0xf694, 0x41f0, 0x96, 0x85, 0xff, 0x5b, 0xb2, 0x60, 0xdf, 0x2e); + + internal static Guid PowerSaver = new Guid(0xa1841308, 0x3541, 0x4fab, 0xbc, 0x81, 0xf7, 0x15, 0x56, 0xf2, 0x0b, 0x4a); + } + + internal class PowerSetting : IDisposable { + /// <summary> + /// The power policy in effect when this instance was constructed. + /// </summary> + private Guid previousPolicy; + + /// <summary> + /// Initializes a new instance of the <see cref="PowerSetting"/> class. + /// </summary> + /// <param name="powerProfile">The power profile.</param> + internal PowerSetting(Guid powerProfile) { + this.previousPolicy = PowerManagment.CurrentPolicy; + if (this.previousPolicy != powerProfile) { + PowerManagment.CurrentPolicy = powerProfile; + } + } + + /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> + public void Dispose() { + if (this.previousPolicy != PowerManagment.CurrentPolicy) { + PowerManagment.CurrentPolicy = this.previousPolicy; + } + } + } + } + } +} diff --git a/src/DotNetOpenAuth.Test/Performance/PerformanceTestUtilities.cs b/src/DotNetOpenAuth.Test/Performance/PerformanceTestUtilities.cs new file mode 100644 index 0000000..5e28732 --- /dev/null +++ b/src/DotNetOpenAuth.Test/Performance/PerformanceTestUtilities.cs @@ -0,0 +1,56 @@ +//----------------------------------------------------------------------- +// <copyright file="PerformanceTestUtilities.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Test.Performance { + using System; + using System.Diagnostics; + using System.Reflection; + using System.Threading; + using DotNetOpenAuth.OpenId.RelyingParty; + using NUnit.Framework; + + internal static class PerformanceTestUtilities { + internal static Stats Baseline; + + static PerformanceTestUtilities() { + Baseline = CollectBaseline(); + TestUtilities.TestLogger.InfoFormat( + "Scaled where EmptyStaticFunction = 1.0 ({0:f1} nsec = 1.0 units)", + Baseline.Median * 1000); + } + + internal static bool IsOptimized(Assembly assembly) { + DebuggableAttribute debugAttribute = (DebuggableAttribute)System.Attribute.GetCustomAttribute(assembly, typeof(System.Diagnostics.DebuggableAttribute)); + return debugAttribute == null || !debugAttribute.IsJITOptimizerDisabled; + } + + private static Stats CollectBaseline() { + using (new HighPerformance()) { + return new MultiSampleCodeTimer(10, 1000).Measure( + "MethodCalls: EmptyStaticFunction()", + 10, + delegate { + Class.EmptyStaticFunction(); + Class.EmptyStaticFunction(); + Class.EmptyStaticFunction(); + Class.EmptyStaticFunction(); + Class.EmptyStaticFunction(); + Class.EmptyStaticFunction(); + Class.EmptyStaticFunction(); + Class.EmptyStaticFunction(); + Class.EmptyStaticFunction(); + Class.EmptyStaticFunction(); + }); + } + } + + private class Class { + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + public static void EmptyStaticFunction() { + } + } + } +} diff --git a/src/DotNetOpenAuth.Test/TestBase.cs b/src/DotNetOpenAuth.Test/TestBase.cs index 8aeca2d..4a6eaca 100644 --- a/src/DotNetOpenAuth.Test/TestBase.cs +++ b/src/DotNetOpenAuth.Test/TestBase.cs @@ -11,6 +11,8 @@ namespace DotNetOpenAuth.Test { using System.Web; using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OAuth.Messages; + using DotNetOpenAuth.OpenId.RelyingParty; + using DotNetOpenAuth.Test.Performance; using log4net; using NUnit.Framework; @@ -65,6 +67,34 @@ namespace DotNetOpenAuth.Test { log4net.LogManager.Shutdown(); } + internal static Stats MeasurePerformance(Action action, float maximumAllowedUnitTime, int samples = 10, int iterations = 100, string name = null) { + if (!PerformanceTestUtilities.IsOptimized(typeof(OpenIdRelyingParty).Assembly)) { + Assert.Inconclusive("Unoptimized code."); + } + + var timer = new MultiSampleCodeTimer(samples, iterations); + Stats stats; + using (new HighPerformance()) { + stats = timer.Measure(name ?? TestContext.CurrentContext.Test.FullName, action); + } + + stats.AdjustForScale(PerformanceTestUtilities.Baseline.Median); + + TestUtilities.TestLogger.InfoFormat( + "Performance counters: median {0}, mean {1}, min {2}, max {3}, stddev {4} ({5}%).", + stats.Median, + stats.Mean, + stats.Minimum, + stats.Maximum, + stats.StandardDeviation, + stats.StandardDeviation / stats.Median * 100); + + Assert.IsTrue(stats.Mean < maximumAllowedUnitTime, "The mean time of {0} exceeded the maximum allowable of {1}.", stats.Mean, maximumAllowedUnitTime); + TestUtilities.TestLogger.InfoFormat("Within {0}% of the maximum allowed time of {1}.", Math.Round((maximumAllowedUnitTime - stats.Mean) / maximumAllowedUnitTime * 100, 1), maximumAllowedUnitTime); + + return stats; + } + /// <summary> /// Sets HttpContext.Current to some empty (but non-null!) value. /// </summary> @@ -73,15 +103,5 @@ namespace DotNetOpenAuth.Test { new HttpRequest("mock", "http://mock", "mock"), new HttpResponse(new StringWriter())); } - -#pragma warning disable 0618 - protected internal static void SuspendLogging() { - LogManager.GetLoggerRepository().Threshold = LogManager.GetLoggerRepository().LevelMap["OFF"]; - } - - protected internal static void ResumeLogging() { - LogManager.GetLoggerRepository().Threshold = LogManager.GetLoggerRepository().LevelMap["ALL"]; - } -#pragma warning restore 0618 } } diff --git a/src/DotNetOpenAuth.sln b/src/DotNetOpenAuth.sln index 2cef1b2..cf7f97e 100644 --- a/src/DotNetOpenAuth.sln +++ b/src/DotNetOpenAuth.sln @@ -9,6 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\doc\README.Bin.html = ..\doc\README.Bin.html ..\doc\README.html = ..\doc\README.html ..\samples\README.html = ..\samples\README.html + ..\nuget\content\web.config.transform = ..\nuget\content\web.config.transform EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Specs", "Specs", "{CD57219F-24F4-4136-8741-6063D0D7A031}" diff --git a/src/DotNetOpenAuth/ComponentModel/ConverterBase.cs b/src/DotNetOpenAuth/ComponentModel/ConverterBase.cs index 980d90f..c41ac47 100644 --- a/src/DotNetOpenAuth/ComponentModel/ConverterBase.cs +++ b/src/DotNetOpenAuth/ComponentModel/ConverterBase.cs @@ -143,6 +143,7 @@ using System.Reflection; /// <exception cref="T:System.NotSupportedException"> /// The conversion cannot be performed. /// </exception> + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.Contracts.__ContractsRuntime.Assume(System.Boolean,System.String,System.String)", Justification = "No localization required.")] public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { Contract.Assume(destinationType != null, "Missing contract."); if (destinationType.IsInstanceOfType(value)) { diff --git a/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd b/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd index 9c0ab77..0b27948 100644 --- a/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd +++ b/src/DotNetOpenAuth/Configuration/DotNetOpenAuth.xsd @@ -218,6 +218,16 @@ </xs:documentation> </xs:annotation> </xs:attribute> + <xs:attribute name="maximumIndirectMessageUrlLength" type="xs:int" default="2048"> + <xs:annotation> + <xs:documentation> + The maximum allowable size for a 301 Redirect response before we send + a 200 OK response with a scripted form POST with the parameters instead + in order to ensure successfully sending a large payload to another server + that might have a maximum allowable size restriction on its GET request. + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> <xs:element name="openid"> @@ -243,6 +253,44 @@ </xs:documentation> </xs:annotation> <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element name="trustedProviders"> + <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element name="add"> + <xs:complexType> + <xs:attribute name="endpoint" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + The OpenID Provider Endpoint (aka "OP Endpoint") that this relying party trusts. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + <xs:element name="remove"> + <xs:complexType> + <xs:attribute name="endpoint" type="xs:string" use="required" /> + </xs:complexType> + </xs:element> + <xs:element name="clear"> + <xs:complexType> + <!--tag is empty--> + </xs:complexType> + </xs:element> + </xs:choice> + <xs:attribute name="rejectAssertionsFromUntrustedProviders" type="xs:boolean" default="false"> + <xs:annotation> + <xs:documentation> + A value indicating whether any login attempt coming from an OpenID Provider Endpoint that is not on this + whitelist of trusted OP Endpoints will be rejected. If the trusted providers list is empty and this value + is true, all assertions are rejected. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + </xs:choice> <xs:attribute name="requireSsl" type="xs:boolean" default="false"> <xs:annotation> <xs:documentation> @@ -269,7 +317,7 @@ <xs:attribute name="minimumHashBitLength" type="xs:int"> <xs:annotation> <xs:documentation> - Shared associations with OpenID Providers will only be formed or used if they + Shared associations with OpenID Providers will only be formed or used if they are willing to form associations equal to or greater than a given level of protection. </xs:documentation> </xs:annotation> @@ -524,7 +572,7 @@ so security is comparable to OpenID 2.0 relying parties. </xs:documentation> </xs:annotation> - </xs:attribute> + </xs:attribute> <xs:attribute name="unsolicitedAssertionVerification"> <xs:annotation> <xs:documentation> @@ -735,7 +783,7 @@ </xs:documentation> </xs:annotation> <xs:complexType> - + </xs:complexType> </xs:element> </xs:choice> @@ -856,7 +904,7 @@ <xs:attribute name="includeEventStatistics" type="xs:boolean" default="true"> <xs:annotation> <xs:documentation> - Whether a set of counters that track how often certain events (such as an + Whether a set of counters that track how often certain events (such as an successful or failed authentication) is included in the report. </xs:documentation> </xs:annotation> diff --git a/src/DotNetOpenAuth/Configuration/MessagingElement.cs b/src/DotNetOpenAuth/Configuration/MessagingElement.cs index f130dbc..28b34a3 100644 --- a/src/DotNetOpenAuth/Configuration/MessagingElement.cs +++ b/src/DotNetOpenAuth/Configuration/MessagingElement.cs @@ -37,6 +37,20 @@ namespace DotNetOpenAuth.Configuration { private const string StrictConfigName = "strict"; /// <summary> + /// The default value for the <see cref="MaximumIndirectMessageUrlLength"/> property. + /// </summary> + /// <value> + /// 2KB, recommended by OpenID group + /// </value> + private const int DefaultMaximumIndirectMessageUrlLength = 2 * 1024; + + /// <summary> + /// The name of the attribute that controls the maximum length of a URL before it is converted + /// to a POST payload. + /// </summary> + private const string MaximumIndirectMessageUrlLengthConfigName = "maximumIndirectMessageUrlLength"; + + /// <summary> /// Gets the actual maximum message lifetime that a program should allow. /// </summary> /// <value>The sum of the <see cref="MaximumMessageLifetime"/> and @@ -114,5 +128,19 @@ namespace DotNetOpenAuth.Configuration { get { return (UntrustedWebRequestElement)this[UntrustedWebRequestElementName] ?? new UntrustedWebRequestElement(); } set { this[UntrustedWebRequestElementName] = value; } } + + /// <summary> + /// Gets or sets the maximum allowable size for a 301 Redirect response before we send + /// a 200 OK response with a scripted form POST with the parameters instead + /// in order to ensure successfully sending a large payload to another server + /// that might have a maximum allowable size restriction on its GET request. + /// </summary> + /// <value>The default value is 2048.</value> + [ConfigurationProperty(MaximumIndirectMessageUrlLengthConfigName, DefaultValue = DefaultMaximumIndirectMessageUrlLength)] + [IntegerValidator(MinValue = 500, MaxValue = 4096)] + internal int MaximumIndirectMessageUrlLength { + get { return (int)this[MaximumIndirectMessageUrlLengthConfigName]; } + set { this[MaximumIndirectMessageUrlLengthConfigName] = value; } + } } } diff --git a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs index 1bf2ebc..4347e2c 100644 --- a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs +++ b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs @@ -81,6 +81,11 @@ namespace DotNetOpenAuth.Configuration { private const string ProtectDownlevelReplayAttacksConfigName = "protectDownlevelReplayAttacks"; /// <summary> + /// The name of the <trustedProviders> sub-element. + /// </summary> + private const string TrustedProvidersElementName = "trustedProviders"; + + /// <summary> /// Initializes a new instance of the <see cref="OpenIdRelyingPartySecuritySettingsElement"/> class. /// </summary> public OpenIdRelyingPartySecuritySettingsElement() { @@ -235,6 +240,16 @@ namespace DotNetOpenAuth.Configuration { } /// <summary> + /// Gets or sets the set of trusted OpenID Provider Endpoints. + /// </summary> + [ConfigurationProperty(TrustedProvidersElementName, IsDefaultCollection = false)] + [ConfigurationCollection(typeof(TrustedProviderConfigurationCollection))] + public TrustedProviderConfigurationCollection TrustedProviders { + get { return (TrustedProviderConfigurationCollection)this[TrustedProvidersElementName] ?? new TrustedProviderConfigurationCollection(); } + set { this[TrustedProvidersElementName] = value; } + } + + /// <summary> /// Initializes a programmatically manipulatable bag of these security settings with the settings from the config file. /// </summary> /// <returns>The newly created security settings object.</returns> @@ -256,6 +271,11 @@ namespace DotNetOpenAuth.Configuration { settings.AllowApproximateIdentifierDiscovery = this.AllowApproximateIdentifierDiscovery; settings.ProtectDownlevelReplayAttacks = this.ProtectDownlevelReplayAttacks; + settings.RejectAssertionsFromUntrustedProviders = this.TrustedProviders.RejectAssertionsFromUntrustedProviders; + foreach (TrustedProviderEndpointConfigurationElement opEndpoint in this.TrustedProviders) { + settings.TrustedProviderEndpoints.Add(opEndpoint.ProviderEndpoint); + } + return settings; } } diff --git a/src/DotNetOpenAuth/Configuration/TrustedProviderConfigurationCollection.cs b/src/DotNetOpenAuth/Configuration/TrustedProviderConfigurationCollection.cs new file mode 100644 index 0000000..f5e62f4 --- /dev/null +++ b/src/DotNetOpenAuth/Configuration/TrustedProviderConfigurationCollection.cs @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------- +// <copyright file="TrustedProviderConfigurationCollection.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Configuration { + using System; + using System.Collections.Generic; + using System.Configuration; + using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; + + /// <summary> + /// A configuration collection of trusted OP Endpoints. + /// </summary> + internal class TrustedProviderConfigurationCollection : ConfigurationElementCollection { + /// <summary> + /// The name of the "rejectAssertionsFromUntrustedProviders" element. + /// </summary> + private const string RejectAssertionsFromUntrustedProvidersConfigName = "rejectAssertionsFromUntrustedProviders"; + + /// <summary> + /// Initializes a new instance of the <see cref="TrustedProviderConfigurationCollection"/> class. + /// </summary> + internal TrustedProviderConfigurationCollection() { + } + + /// <summary> + /// Initializes a new instance of the <see cref="TrustedProviderConfigurationCollection"/> class. + /// </summary> + /// <param name="elements">The elements to initialize the collection with.</param> + [SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "Seems unavoidable")] + internal TrustedProviderConfigurationCollection(IEnumerable<TrustedProviderEndpointConfigurationElement> elements) { + Contract.Requires<ArgumentNullException>(elements != null); + + foreach (TrustedProviderEndpointConfigurationElement element in elements) { + this.BaseAdd(element); + } + } + + /// <summary> + /// Gets or sets a value indicating whether any login attempt coming from an OpenID Provider Endpoint that is not on this + /// whitelist of trusted OP Endpoints will be rejected. If the trusted providers list is empty and this value + /// is true, all assertions are rejected. + /// </summary> + [ConfigurationProperty(RejectAssertionsFromUntrustedProvidersConfigName, DefaultValue = false)] + internal bool RejectAssertionsFromUntrustedProviders { + get { return (bool)this[RejectAssertionsFromUntrustedProvidersConfigName]; } + set { this[RejectAssertionsFromUntrustedProvidersConfigName] = value; } + } + + /// <summary> + /// When overridden in a derived class, creates a new <see cref="T:System.Configuration.ConfigurationElement"/>. + /// </summary> + /// <returns> + /// A new <see cref="T:System.Configuration.ConfigurationElement"/>. + /// </returns> + protected override ConfigurationElement CreateNewElement() { + return new TrustedProviderEndpointConfigurationElement(); + } + + /// <summary> + /// Gets the element key for a specified configuration element when overridden in a derived class. + /// </summary> + /// <param name="element">The <see cref="T:System.Configuration.ConfigurationElement"/> to return the key for.</param> + /// <returns> + /// An <see cref="T:System.Object"/> that acts as the key for the specified <see cref="T:System.Configuration.ConfigurationElement"/>. + /// </returns> + protected override object GetElementKey(ConfigurationElement element) { + return ((TrustedProviderEndpointConfigurationElement)element).ProviderEndpoint; + } + } +} diff --git a/src/DotNetOpenAuth/Configuration/TrustedProviderEndpointConfigurationElement.cs b/src/DotNetOpenAuth/Configuration/TrustedProviderEndpointConfigurationElement.cs new file mode 100644 index 0000000..2576eb0 --- /dev/null +++ b/src/DotNetOpenAuth/Configuration/TrustedProviderEndpointConfigurationElement.cs @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------- +// <copyright file="TrustedProviderEndpointConfigurationElement.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Configuration { + using System; + using System.Configuration; + + /// <summary> + /// A configuration element that records a trusted Provider Endpoint. + /// </summary> + internal class TrustedProviderEndpointConfigurationElement : ConfigurationElement { + /// <summary> + /// The name of the attribute that stores the <see cref="ProviderEndpoint"/> value. + /// </summary> + private const string ProviderEndpointConfigName = "endpoint"; + + /// <summary> + /// Initializes a new instance of the <see cref="TrustedProviderEndpointConfigurationElement"/> class. + /// </summary> + public TrustedProviderEndpointConfigurationElement() { + } + + /// <summary> + /// Gets or sets the OpenID Provider Endpoint (aka "OP Endpoint") that this relying party trusts. + /// </summary> + [ConfigurationProperty(ProviderEndpointConfigName, IsRequired = true, IsKey = true)] + public Uri ProviderEndpoint { + get { return (Uri)this[ProviderEndpointConfigName]; } + set { this[ProviderEndpointConfigName] = value; } + } + } +} diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index 488e092..52930af 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -84,7 +84,7 @@ http://opensource.org/licenses/ms-pl.html <CodeContractsEmitXMLDocs>True</CodeContractsEmitXMLDocs> <CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions> <CodeContractsReferenceAssembly>Build</CodeContractsReferenceAssembly> - <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> + <CodeAnalysisRuleSet>Migrated rules for DotNetOpenAuth.ruleset</CodeAnalysisRuleSet> <CodeContractsExtraRewriteOptions /> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> @@ -125,7 +125,7 @@ http://opensource.org/licenses/ms-pl.html <CodeContractsEmitXMLDocs>True</CodeContractsEmitXMLDocs> <CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions> <CodeContractsReferenceAssembly>Build</CodeContractsReferenceAssembly> - <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet> + <CodeAnalysisRuleSet>Migrated rules for DotNetOpenAuth.ruleset</CodeAnalysisRuleSet> <CodeContractsExtraRewriteOptions /> </PropertyGroup> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ReleaseNoUI|AnyCPU'"> @@ -165,6 +165,7 @@ http://opensource.org/licenses/ms-pl.html <CodeContractsRedundantAssumptions>False</CodeContractsRedundantAssumptions> <CodeContractsReferenceAssembly>Build</CodeContractsReferenceAssembly> <CodeContractsExtraRewriteOptions /> + <CodeAnalysisRuleSet>Migrated rules for DotNetOpenAuth.ruleset</CodeAnalysisRuleSet> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'CodeAnalysis|AnyCPU' "> <DebugSymbols>true</DebugSymbols> @@ -181,7 +182,7 @@ http://opensource.org/licenses/ms-pl.html </CodeContractsCustomRewriterAssembly> <CodeContractsCustomRewriterClass> </CodeContractsCustomRewriterClass> - <CodeContractsRuntimeCheckingLevel>Full</CodeContractsRuntimeCheckingLevel> + <CodeContractsRuntimeCheckingLevel>Preconditions</CodeContractsRuntimeCheckingLevel> <CodeContractsRunCodeAnalysis>True</CodeContractsRunCodeAnalysis> <CodeContractsBuildReferenceAssembly>True</CodeContractsBuildReferenceAssembly> <CodeContractsNonNullObligations>False</CodeContractsNonNullObligations> @@ -295,6 +296,8 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="Configuration\OpenIdRelyingPartyElement.cs" /> <Compile Include="Configuration\OpenIdRelyingPartySecuritySettingsElement.cs" /> <Compile Include="Configuration\ReportingElement.cs" /> + <Compile Include="Configuration\TrustedProviderConfigurationCollection.cs" /> + <Compile Include="Configuration\TrustedProviderEndpointConfigurationElement.cs" /> <Compile Include="Configuration\TypeConfigurationCollection.cs" /> <Compile Include="Configuration\TypeConfigurationElement.cs" /> <Compile Include="Configuration\UntrustedWebRequestElement.cs" /> @@ -316,6 +319,7 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="InfoCard\WellKnownIssuers.cs" /> <Compile Include="Messaging\CachedDirectWebResponse.cs" /> <Compile Include="Messaging\ChannelContract.cs" /> + <Compile Include="Messaging\IMessageOriginalPayload.cs" /> <Compile Include="Messaging\DirectWebRequestOptions.cs" /> <Compile Include="Messaging\EnumerableCache.cs" /> <Compile Include="Messaging\HostErrorException.cs" /> @@ -795,10 +799,11 @@ http://opensource.org/licenses/ms-pl.html OutputFile="$(ILMergeOutputAssembly)" KeyFile="$(PublicKeyFile)" DelaySign="true" + ToolPath="$(ProjectRoot)tools\ILMerge" TargetPlatformVersion="$(ClrVersion).0" TargetPlatformDirectory="$(ILMergeTargetPlatformDirectory)" /> </Target> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.targets" /> <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))\EnlistmentInfo.targets" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))' != '' " /> -</Project>
\ No newline at end of file +</Project> diff --git a/src/DotNetOpenAuth/GlobalSuppressions.cs b/src/DotNetOpenAuth/GlobalSuppressions.cs index 9b1bcfa..8539422 100644 --- a/src/DotNetOpenAuth/GlobalSuppressions.cs +++ b/src/DotNetOpenAuth/GlobalSuppressions.cs @@ -9,54 +9,61 @@ // "In Project Suppression File". // You do not need to add suppressions to this file manually. -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sha", Scope = "type", Target = "DotNetOpenAuth.OAuth.ChannelElements.HmacSha1SigningBindingElement")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Hmac", Scope = "type", Target = "DotNetOpenAuth.OAuth.ChannelElements.HmacSha1SigningBindingElement")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Rsa", Scope = "type", Target = "DotNetOpenAuth.OAuth.ChannelElements.RsaSha1SigningBindingElement")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sha", Scope = "type", Target = "DotNetOpenAuth.OAuth.ChannelElements.RsaSha1SigningBindingElement")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "Diffie-Hellman", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "checkInput", Scope = "member", Target = "Org.Mentalis.Security.Cryptography.DiffieHellmanManaged.#Initialize(Mono.Math.BigInteger,Mono.Math.BigInteger,Mono.Math.BigInteger,System.Int32,System.Boolean)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Scope = "member", Target = "Org.Mentalis.Security.Cryptography.DiffieHellmanManaged.#.ctor(System.Int32,System.Int32,Org.Mentalis.Security.Cryptography.DHKeyGeneration)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Scope = "member", Target = "Org.Mentalis.Security.Cryptography.DiffieHellmanManaged.#.ctor(System.Byte[],System.Byte[],System.Int32)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Scope = "member", Target = "Org.Mentalis.Security.Cryptography.DiffieHellmanManaged.#.ctor(System.Byte[],System.Byte[],System.Byte[])")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Scope = "member", Target = "Org.Mentalis.Security.Cryptography.DiffieHellman.#FromXmlString(System.String)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "PostTrialDivisionTest", Scope = "member", Target = "Mono.Math.Prime.Generator.SequentialSearchPrimeGeneratorBase.#GenerateNewPrime(System.Int32,System.Object)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "Mono.Math.Prime.PrimalityTests.#GetSPPRounds(Mono.Math.BigInteger,Mono.Math.Prime.ConfidenceFactor)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "Mono.Math.BigInteger+ModulusRing.#BarrettReduction(Mono.Math.BigInteger)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "Mono.Math.BigInteger.#op_Multiply(Mono.Math.BigInteger,Mono.Math.BigInteger)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "sign", Scope = "member", Target = "Mono.Math.BigInteger.#.ctor(Mono.Math.BigInteger+Sign,System.UInt32)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "notUsed", Scope = "member", Target = "Mono.Math.BigInteger.#isProbablePrime(System.Int32)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "returnto", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "openid", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "claimedid", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Xri", Scope = "type", Target = "DotNetOpenAuth.OpenId.XriIdentifier")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "yyyy-MM-dd", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "usersetupurl", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "birthdate", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "rehydrated", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.Messaging.Bindings")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.ChannelElements")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.Extensions")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.Extensions.SimpleRegistration")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.Messages")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames", Justification = "We sign it when producing drops.")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.Extensions.OAuth")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.Extensions.UI")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.Messaging.Reflection")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "oauthverifier", Scope = "resource", Target = "DotNetOpenAuth.OAuth.OAuthStrings.resources")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "whitelist", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "icam", Scope = "resource", Target = "DotNetOpenAuth.OpenId.Behaviors.BehaviorStrings.resources")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "idmanagement", Scope = "resource", Target = "DotNetOpenAuth.OpenId.Behaviors.BehaviorStrings.resources")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "no-pii", Scope = "resource", Target = "DotNetOpenAuth.OpenId.Behaviors.BehaviorStrings.resources")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "req", Scope = "member", Target = "DotNetOpenAuth.OpenId.Provider.IAuthenticationRequestContract.#DotNetOpenAuth.OpenId.Provider.IAuthenticationRequest.ClaimedIdentifier")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "runat", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.RelyingParty.IRelyingPartyBehavior.OnOutgoingAuthenticationRequest(DotNetOpenAuth.OpenId.RelyingParty.IAuthenticationRequest)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.RelyingParty.IRelyingPartyBehavior.OnIncomingPositiveAssertion(DotNetOpenAuth.OpenId.RelyingParty.IAuthenticationResponse)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.RelyingParty.IRelyingPartyBehavior.ApplySecuritySettings(DotNetOpenAuth.OpenId.RelyingParty.RelyingPartySecuritySettings)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.Provider.IProviderBehavior.OnOutgoingResponse(DotNetOpenAuth.OpenId.Provider.IAuthenticationRequest)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.Provider.IProviderBehavior.OnIncomingRequest(DotNetOpenAuth.OpenId.Provider.IRequest)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.Provider.IProviderBehavior.ApplySecuritySettings(DotNetOpenAuth.OpenId.Provider.ProviderSecuritySettings)")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.Mvc")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Mvc", Scope = "namespace", Target = "DotNetOpenAuth.Mvc")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework", MessageId = "System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")] +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sha", Scope = "type", Target = "DotNetOpenAuth.OAuth.ChannelElements.HmacSha1SigningBindingElement")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Hmac", Scope = "type", Target = "DotNetOpenAuth.OAuth.ChannelElements.HmacSha1SigningBindingElement")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Rsa", Scope = "type", Target = "DotNetOpenAuth.OAuth.ChannelElements.RsaSha1SigningBindingElement")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sha", Scope = "type", Target = "DotNetOpenAuth.OAuth.ChannelElements.RsaSha1SigningBindingElement")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "Diffie-Hellman", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] +[assembly: SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "checkInput", Scope = "member", Target = "Org.Mentalis.Security.Cryptography.DiffieHellmanManaged.#Initialize(Mono.Math.BigInteger,Mono.Math.BigInteger,Mono.Math.BigInteger,System.Int32,System.Boolean)")] +[assembly: SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Scope = "member", Target = "Org.Mentalis.Security.Cryptography.DiffieHellmanManaged.#.ctor(System.Int32,System.Int32,Org.Mentalis.Security.Cryptography.DHKeyGeneration)")] +[assembly: SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Scope = "member", Target = "Org.Mentalis.Security.Cryptography.DiffieHellmanManaged.#.ctor(System.Byte[],System.Byte[],System.Int32)")] +[assembly: SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Scope = "member", Target = "Org.Mentalis.Security.Cryptography.DiffieHellmanManaged.#.ctor(System.Byte[],System.Byte[],System.Byte[])")] +[assembly: SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Scope = "member", Target = "Org.Mentalis.Security.Cryptography.DiffieHellman.#FromXmlString(System.String)")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "PostTrialDivisionTest", Scope = "member", Target = "Mono.Math.Prime.Generator.SequentialSearchPrimeGeneratorBase.#GenerateNewPrime(System.Int32,System.Object)")] +[assembly: SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "Mono.Math.Prime.PrimalityTests.#GetSPPRounds(Mono.Math.BigInteger,Mono.Math.Prime.ConfidenceFactor)")] +[assembly: SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "Mono.Math.BigInteger+ModulusRing.#BarrettReduction(Mono.Math.BigInteger)")] +[assembly: SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "Mono.Math.BigInteger.#op_Multiply(Mono.Math.BigInteger,Mono.Math.BigInteger)")] +[assembly: SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "sign", Scope = "member", Target = "Mono.Math.BigInteger.#.ctor(Mono.Math.BigInteger+Sign,System.UInt32)")] +[assembly: SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "notUsed", Scope = "member", Target = "Mono.Math.BigInteger.#isProbablePrime(System.Int32)")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "returnto", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "openid", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "claimedid", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Xri", Scope = "type", Target = "DotNetOpenAuth.OpenId.XriIdentifier")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "yyyy-MM-dd", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "usersetupurl", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "birthdate", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "rehydrated", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] +[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.Messaging.Bindings")] +[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.ChannelElements")] +[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.Extensions")] +[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.Extensions.SimpleRegistration")] +[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.Messages")] +[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy")] +[assembly: SuppressMessage("Microsoft.Design", "CA2210:AssembliesShouldHaveValidStrongNames", Justification = "We sign it when producing drops.")] +[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.Extensions.OAuth")] +[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.OpenId.Extensions.UI")] +[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.Messaging.Reflection")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "oauthverifier", Scope = "resource", Target = "DotNetOpenAuth.OAuth.OAuthStrings.resources")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "whitelist", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "icam", Scope = "resource", Target = "DotNetOpenAuth.OpenId.Behaviors.BehaviorStrings.resources")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "idmanagement", Scope = "resource", Target = "DotNetOpenAuth.OpenId.Behaviors.BehaviorStrings.resources")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "no-pii", Scope = "resource", Target = "DotNetOpenAuth.OpenId.Behaviors.BehaviorStrings.resources")] +[assembly: SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "req", Scope = "member", Target = "DotNetOpenAuth.OpenId.Provider.IAuthenticationRequestContract.#DotNetOpenAuth.OpenId.Provider.IAuthenticationRequest.ClaimedIdentifier")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "runat", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] +[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.RelyingParty.IRelyingPartyBehavior.OnOutgoingAuthenticationRequest(DotNetOpenAuth.OpenId.RelyingParty.IAuthenticationRequest)")] +[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.RelyingParty.IRelyingPartyBehavior.OnIncomingPositiveAssertion(DotNetOpenAuth.OpenId.RelyingParty.IAuthenticationResponse)")] +[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.RelyingParty.IRelyingPartyBehavior.ApplySecuritySettings(DotNetOpenAuth.OpenId.RelyingParty.RelyingPartySecuritySettings)")] +[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.Provider.IProviderBehavior.OnOutgoingResponse(DotNetOpenAuth.OpenId.Provider.IAuthenticationRequest)")] +[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.Provider.IProviderBehavior.OnIncomingRequest(DotNetOpenAuth.OpenId.Provider.IRequest)")] +[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform.#DotNetOpenAuth.OpenId.Provider.IProviderBehavior.ApplySecuritySettings(DotNetOpenAuth.OpenId.Provider.ProviderSecuritySettings)")] +[assembly: SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly")] +[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "DotNetOpenAuth.Mvc")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Mvc", Scope = "namespace", Target = "DotNetOpenAuth.Mvc")] +[assembly: SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework", MessageId = "System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")] +[assembly: SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "DotNetOpenAuth.OpenId.DiffieHellmanUtilities.#.cctor()")] +[assembly: SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Scope = "member", Target = "DotNetOpenAuth.OAuth.Messages.SignedMessageBase.#DotNetOpenAuth.Messaging.IMessageOriginalPayload.OriginalPayload")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "iframe", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "Sig", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] +[assembly: SuppressMessage("Microsoft.Naming", "CA1701:ResourceStringCompoundWordsShouldBeCasedCorrectly", MessageId = "DSig", Scope = "resource", Target = "DotNetOpenAuth.OpenId.OpenIdStrings.resources")] diff --git a/src/DotNetOpenAuth/InfoCard/ReceivingTokenEventArgs.cs b/src/DotNetOpenAuth/InfoCard/ReceivingTokenEventArgs.cs index 2ac2b7e..3dd892a 100644 --- a/src/DotNetOpenAuth/InfoCard/ReceivingTokenEventArgs.cs +++ b/src/DotNetOpenAuth/InfoCard/ReceivingTokenEventArgs.cs @@ -71,6 +71,7 @@ namespace DotNetOpenAuth.InfoCard { /// Adds an X.509 certificate with a private key that may be used to decrypt the incoming token. /// </summary> /// <param name="certificate">The certificate.</param> + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "False positive")] public void AddDecryptingToken(X509Certificate2 certificate) { Contract.Requires<ArgumentNullException>(certificate != null); Contract.Requires<ArgumentException>(certificate.HasPrivateKey); diff --git a/src/DotNetOpenAuth/InfoCard/Token/Token.cs b/src/DotNetOpenAuth/InfoCard/Token/Token.cs index 89fa3a3..3b6f573 100644 --- a/src/DotNetOpenAuth/InfoCard/Token/Token.cs +++ b/src/DotNetOpenAuth/InfoCard/Token/Token.cs @@ -41,6 +41,7 @@ namespace DotNetOpenAuth.InfoCard { /// <param name="audience">The audience. May be <c>null</c> to avoid audience checking.</param> /// <param name="decryptor">The decryptor to use to decrypt the token, if necessary..</param> /// <exception cref="InformationCardException">Thrown for any problem decoding or decrypting the token.</exception> + [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "Not a problem for this type."), SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "False positive")] private Token(string tokenXml, Uri audience, TokenDecryptor decryptor) { Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(tokenXml)); Contract.Requires<ArgumentException>(decryptor != null || !IsEncrypted(tokenXml)); @@ -64,7 +65,14 @@ namespace DotNetOpenAuth.InfoCard { } } - this.Xml = new XPathDocument(new StringReader(decryptedString)).CreateNavigator(); + var stringReader = new StringReader(decryptedString); + try { + this.Xml = new XPathDocument(stringReader).CreateNavigator(); + } catch { + stringReader.Dispose(); + throw; + } + Logger.InfoCard.DebugFormat("Incoming SAML token, after any decryption: {0}", this.Xml.InnerXml); this.AuthorizationContext = TokenUtility.AuthenticateToken(this.Xml.ReadSubtree(), audience); } @@ -191,13 +199,26 @@ namespace DotNetOpenAuth.InfoCard { /// <returns> /// <c>true</c> if the specified token XML is encrypted; otherwise, <c>false</c>. /// </returns> - [Pure] + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "False positive"), Pure] internal static bool IsEncrypted(string tokenXml) { Contract.Requires<ArgumentNullException>(tokenXml != null); - using (XmlReader tokenReader = XmlReader.Create(new StringReader(tokenXml))) { + var stringReader = new StringReader(tokenXml); + XmlReader tokenReader; + try { + tokenReader = XmlReader.Create(stringReader); + } catch { + stringReader.Dispose(); + throw; + } + + try { Contract.Assume(tokenReader != null); // CC missing for XmlReader.Create return IsEncrypted(tokenReader); + } catch { + IDisposable disposableReader = tokenReader; + disposableReader.Dispose(); + throw; } } diff --git a/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs b/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs index c9bc1d3..111d636 100644 --- a/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs +++ b/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs @@ -7,6 +7,7 @@ namespace DotNetOpenAuth.Messaging { using System; using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.IO; using System.Net; @@ -156,6 +157,7 @@ namespace DotNetOpenAuth.Messaging { /// <param name="response">The response whose stream is to be cloned.</param> /// <param name="maximumBytesToRead">The maximum bytes to cache.</param> /// <returns>The seekable Stream instance that contains a copy of what was returned in the HTTP response.</returns> + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.Contracts.__ContractsRuntime.Assume(System.Boolean,System.String,System.String)", Justification = "No localization required.")] private static MemoryStream CacheNetworkStreamAndClose(HttpWebResponse response, int maximumBytesToRead) { Contract.Requires<ArgumentNullException>(response != null); Contract.Ensures(Contract.Result<MemoryStream>() != null); @@ -163,15 +165,20 @@ namespace DotNetOpenAuth.Messaging { // Now read and cache the network stream Stream networkStream = response.GetResponseStream(); MemoryStream cachedStream = new MemoryStream(response.ContentLength < 0 ? 4 * 1024 : Math.Min((int)response.ContentLength, maximumBytesToRead)); - Contract.Assume(networkStream.CanRead, "HttpWebResponse.GetResponseStream() always returns a readable stream."); // CC missing - Contract.Assume(cachedStream.CanWrite, "This is a MemoryStream -- it's always writable."); // CC missing - networkStream.CopyTo(cachedStream); - cachedStream.Seek(0, SeekOrigin.Begin); - - networkStream.Dispose(); - response.Close(); - - return cachedStream; + try { + Contract.Assume(networkStream.CanRead, "HttpWebResponse.GetResponseStream() always returns a readable stream."); // CC missing + Contract.Assume(cachedStream.CanWrite, "This is a MemoryStream -- it's always writable."); // CC missing + networkStream.CopyTo(cachedStream); + cachedStream.Seek(0, SeekOrigin.Begin); + + networkStream.Dispose(); + response.Close(); + + return cachedStream; + } catch { + cachedStream.Dispose(); + throw; + } } } } diff --git a/src/DotNetOpenAuth/Messaging/Channel.cs b/src/DotNetOpenAuth/Messaging/Channel.cs index 84dbe3c..0abdcfa 100644 --- a/src/DotNetOpenAuth/Messaging/Channel.cs +++ b/src/DotNetOpenAuth/Messaging/Channel.cs @@ -25,6 +25,7 @@ namespace DotNetOpenAuth.Messaging { /// <summary> /// Manages sending direct messages to a remote party and receiving responses. /// </summary> + [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Unavoidable.")] [ContractVerification(true)] [ContractClass(typeof(ChannelContract))] public abstract class Channel : IDisposable { @@ -47,14 +48,6 @@ namespace DotNetOpenAuth.Messaging { protected internal static readonly ContentType HttpFormUrlEncodedContentType = new ContentType(HttpFormUrlEncoded) { CharSet = PostEntityEncoding.WebName }; /// <summary> - /// The maximum allowable size for a 301 Redirect response before we send - /// a 200 OK response with a scripted form POST with the parameters instead - /// in order to ensure successfully sending a large payload to another server - /// that might have a maximum allowable size restriction on its GET request. - /// </summary> - private const int IndirectMessageGetToPostThreshold = 2 * 1024; // 2KB, recommended by OpenID group - - /// <summary> /// The HTML that should be returned to the user agent as part of a 301 Redirect. /// </summary> /// <value>A string that should be used as the first argument to String.Format, where the {0} should be replaced with the URL to redirect to.</value> @@ -120,6 +113,11 @@ namespace DotNetOpenAuth.Messaging { private RequestCachePolicy cachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore); /// <summary> + /// Backing field for the <see cref="MaximumIndirectMessageUrlLength"/> property. + /// </summary> + private int maximumIndirectMessageUrlLength = Configuration.DotNetOpenAuthSection.Configuration.Messaging.MaximumIndirectMessageUrlLength; + + /// <summary> /// Initializes a new instance of the <see cref="Channel"/> class. /// </summary> /// <param name="messageTypeProvider"> @@ -157,6 +155,24 @@ namespace DotNetOpenAuth.Messaging { public IDirectWebRequestHandler WebRequestHandler { get; set; } /// <summary> + /// Gets or sets the maximum allowable size for a 301 Redirect response before we send + /// a 200 OK response with a scripted form POST with the parameters instead + /// in order to ensure successfully sending a large payload to another server + /// that might have a maximum allowable size restriction on its GET request. + /// </summary> + /// <value>The default value is 2048.</value> + public int MaximumIndirectMessageUrlLength { + get { + return this.maximumIndirectMessageUrlLength; + } + + set { + Contract.Requires<ArgumentOutOfRangeException>(value >= 500 && value <= 4096); + this.maximumIndirectMessageUrlLength = value; + } + } + + /// <summary> /// Gets or sets the message descriptions. /// </summary> internal MessageDescriptionCollection MessageDescriptions { @@ -730,7 +746,7 @@ namespace DotNetOpenAuth.Messaging { // First try creating a 301 redirect, and fallback to a form POST // if the message is too big. OutgoingWebResponse response = this.Create301RedirectResponse(message, fields); - if (response.Headers[HttpResponseHeader.Location].Length > IndirectMessageGetToPostThreshold) { + if (response.Headers[HttpResponseHeader.Location].Length > this.MaximumIndirectMessageUrlLength) { response = this.CreateFormPostResponse(message, fields); } diff --git a/src/DotNetOpenAuth/Messaging/IMessageOriginalPayload.cs b/src/DotNetOpenAuth/Messaging/IMessageOriginalPayload.cs new file mode 100644 index 0000000..d18be20 --- /dev/null +++ b/src/DotNetOpenAuth/Messaging/IMessageOriginalPayload.cs @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------- +// <copyright file="IMessageOriginalPayload.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Messaging { + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; + using System.Text; + + /// <summary> + /// An interface that appears on messages that need to retain a description of + /// what their literal payload was when they were deserialized. + /// </summary> + [ContractClass(typeof(IMessageOriginalPayloadContract))] + public interface IMessageOriginalPayload { + /// <summary> + /// Gets or sets the original message parts, before any normalization or default values were assigned. + /// </summary> + [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "By design")] + IDictionary<string, string> OriginalPayload { get; set; } + } + + /// <summary> + /// Code contract for the <see cref="IMessageOriginalPayload"/> interface. + /// </summary> + [ContractClassFor(typeof(IMessageOriginalPayload))] + internal abstract class IMessageOriginalPayloadContract : IMessageOriginalPayload { + /// <summary> + /// Gets or sets the original message parts, before any normalization or default values were assigned. + /// </summary> + IDictionary<string, string> IMessageOriginalPayload.OriginalPayload { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + } +} diff --git a/src/DotNetOpenAuth/Messaging/MessageSerializer.cs b/src/DotNetOpenAuth/Messaging/MessageSerializer.cs index ccda5d5..e1625b9 100644 --- a/src/DotNetOpenAuth/Messaging/MessageSerializer.cs +++ b/src/DotNetOpenAuth/Messaging/MessageSerializer.cs @@ -106,7 +106,13 @@ namespace DotNetOpenAuth.Messaging { } catch (ArgumentException ex) { throw ErrorUtilities.Wrap(ex, MessagingStrings.ErrorDeserializingMessage, this.messageType.Name); } + messageDictionary.Message.EnsureValidMessage(); + + var originalPayloadMessage = messageDictionary.Message as IMessageOriginalPayload; + if (originalPayloadMessage != null) { + originalPayloadMessage.OriginalPayload = fields; + } } #if CONTRACTS_FULL diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs index 85ba9a9..4b20ab8 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs +++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs @@ -444,9 +444,14 @@ namespace DotNetOpenAuth.Messaging { Contract.Requires<ArgumentException>(copyFrom.CanRead); MemoryStream copyTo = new MemoryStream(copyFrom.CanSeek ? (int)copyFrom.Length : 4 * 1024); - copyFrom.CopyTo(copyTo); - copyTo.Position = 0; - return copyTo; + try { + copyFrom.CopyTo(copyTo); + copyTo.Position = 0; + return copyTo; + } catch { + copyTo.Dispose(); + throw; + } } /// <summary> diff --git a/src/DotNetOpenAuth/Messaging/MultipartPostPart.cs b/src/DotNetOpenAuth/Messaging/MultipartPostPart.cs index f9a5988..eff40dd 100644 --- a/src/DotNetOpenAuth/Messaging/MultipartPostPart.cs +++ b/src/DotNetOpenAuth/Messaging/MultipartPostPart.cs @@ -40,10 +40,10 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> - /// Gets the content disposition. + /// Gets or sets the content disposition. /// </summary> /// <value>The content disposition.</value> - public string ContentDisposition { get; private set; } + public string ContentDisposition { get; set; } /// <summary> /// Gets the key=value attributes that appear on the same line as the Content-Disposition. @@ -103,9 +103,14 @@ namespace DotNetOpenAuth.Messaging { Contract.Requires<ArgumentException>(value != null); var part = new MultipartPostPart("form-data"); - part.ContentAttributes["name"] = name; - part.Content = new MemoryStream(Encoding.UTF8.GetBytes(value)); - return part; + try { + part.ContentAttributes["name"] = name; + part.Content = new MemoryStream(Encoding.UTF8.GetBytes(value)); + return part; + } catch { + part.Dispose(); + throw; + } } /// <summary> @@ -121,7 +126,13 @@ namespace DotNetOpenAuth.Messaging { Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(contentType)); string fileName = Path.GetFileName(filePath); - return CreateFormFilePart(name, fileName, contentType, File.OpenRead(filePath)); + var fileStream = File.OpenRead(filePath); + try { + return CreateFormFilePart(name, fileName, contentType, fileStream); + } catch { + fileStream.Dispose(); + throw; + } } /// <summary> @@ -139,14 +150,20 @@ namespace DotNetOpenAuth.Messaging { Contract.Requires<ArgumentException>(content != null); var part = new MultipartPostPart("file"); - part.ContentAttributes["name"] = name; - part.ContentAttributes["filename"] = fileName; - part.PartHeaders[HttpRequestHeader.ContentType] = contentType; - if (!contentType.StartsWith("text/", StringComparison.Ordinal)) { - part.PartHeaders["Content-Transfer-Encoding"] = "binary"; + try { + part.ContentAttributes["name"] = name; + part.ContentAttributes["filename"] = fileName; + part.PartHeaders[HttpRequestHeader.ContentType] = contentType; + if (!contentType.StartsWith("text/", StringComparison.Ordinal)) { + part.PartHeaders["Content-Transfer-Encoding"] = "binary"; + } + + part.Content = content; + return part; + } catch { + part.Dispose(); + throw; } - part.Content = content; - return part; } /// <summary> diff --git a/src/DotNetOpenAuth/Messaging/NetworkDirectWebResponse.cs b/src/DotNetOpenAuth/Messaging/NetworkDirectWebResponse.cs index 643cf81..800d2ba 100644 --- a/src/DotNetOpenAuth/Messaging/NetworkDirectWebResponse.cs +++ b/src/DotNetOpenAuth/Messaging/NetworkDirectWebResponse.cs @@ -109,6 +109,8 @@ namespace DotNetOpenAuth.Messaging { this.httpWebResponse = null; } } + + base.Dispose(disposing); } } } diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs index 125742c..26d088b 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs @@ -33,6 +33,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { /// <param name="messageType">A type that implements <see cref="IMessage"/>.</param> /// <param name="messageVersion">The protocol version of the message.</param> /// <returns>A <see cref="MessageDescription"/> instance.</returns> + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.Contracts.__ContractsRuntime.Assume(System.Boolean,System.String,System.String)", Justification = "No localization required.")] [Pure] internal MessageDescription Get(Type messageType, Version messageVersion) { Contract.Requires<ArgumentNullException>(messageType != null); diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs index b876ec4..088aa05 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs @@ -112,7 +112,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { /// The attribute discovered on <paramref name="member"/> that describes the /// serialization requirements of the message part. /// </param> - [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Code contracts requires it.")] + [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "Unavoidable"), SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Code contracts requires it.")] internal MessagePart(MemberInfo member, MessagePartAttribute attribute) { Contract.Requires<ArgumentNullException>(member != null); Contract.Requires<ArgumentException>(member is FieldInfo || member is PropertyInfo); @@ -295,6 +295,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { /// <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> + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.Contracts.__ContractsRuntime.Requires<System.ArgumentNullException>(System.Boolean,System.String,System.String)", Justification = "Code contracts"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "toString", Justification = "Code contracts"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "toValue", Justification = "Code contracts")] 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"); diff --git a/src/DotNetOpenAuth/Migrated rules for DotNetOpenAuth.ruleset b/src/DotNetOpenAuth/Migrated rules for DotNetOpenAuth.ruleset index db238b6..0ba4e6e 100644 --- a/src/DotNetOpenAuth/Migrated rules for DotNetOpenAuth.ruleset +++ b/src/DotNetOpenAuth/Migrated rules for DotNetOpenAuth.ruleset @@ -5,6 +5,7 @@ <Rule Id="CA1054" Action="None" /> <Rule Id="CA1055" Action="None" /> <Rule Id="CA1056" Action="None" /> + <Rule Id="CA1062" Action="None" /> <Rule Id="CA2104" Action="None" /> </Rules> </RuleSet>
\ No newline at end of file diff --git a/src/DotNetOpenAuth/Mvc/OpenIdHelper.cs b/src/DotNetOpenAuth/Mvc/OpenIdHelper.cs index ffde271..adde6b6 100644 --- a/src/DotNetOpenAuth/Mvc/OpenIdHelper.cs +++ b/src/DotNetOpenAuth/Mvc/OpenIdHelper.cs @@ -7,6 +7,7 @@ namespace DotNetOpenAuth.Mvc { using System; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Globalization; using System.IO; @@ -35,10 +36,11 @@ namespace DotNetOpenAuth.Mvc { Contract.Requires<ArgumentNullException>(html != null); Contract.Ensures(Contract.Result<string>() != null); - StringWriter result = new StringWriter(); - result.WriteStylesheetLink(OpenId.RelyingParty.OpenIdSelector.EmbeddedStylesheetResourceName); - result.WriteStylesheetLink(OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedStylesheetResourceName); - return result.ToString(); + using (var result = new StringWriter(CultureInfo.CurrentCulture)) { + result.WriteStylesheetLink(OpenId.RelyingParty.OpenIdSelector.EmbeddedStylesheetResourceName); + result.WriteStylesheetLink(OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedStylesheetResourceName); + return result.ToString(); + } } /// <summary> @@ -59,43 +61,45 @@ namespace DotNetOpenAuth.Mvc { /// <returns> /// HTML that should be sent directly to the browser. /// </returns> + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "False positive")] public static string OpenIdSelectorScripts(this HtmlHelper html, OpenIdSelector selectorOptions, OpenIdAjaxOptions additionalOptions) { Contract.Requires<ArgumentNullException>(html != null); Contract.Ensures(Contract.Result<string>() != null); + bool selectorOptionsOwned = false; if (selectorOptions == null) { + selectorOptionsOwned = true; selectorOptions = new OpenId.RelyingParty.OpenIdSelector(); } + try { + if (additionalOptions == null) { + additionalOptions = new OpenIdAjaxOptions(); + } - if (additionalOptions == null) { - additionalOptions = new OpenIdAjaxOptions(); - } - - StringWriter result = new StringWriter(); - - if (additionalOptions.ShowDiagnosticIFrame || additionalOptions.ShowDiagnosticTrace) { - string scriptFormat = @"window.openid_visible_iframe = {0}; // causes the hidden iframe to show up + using (StringWriter result = new StringWriter(CultureInfo.CurrentCulture)) { + if (additionalOptions.ShowDiagnosticIFrame || additionalOptions.ShowDiagnosticTrace) { + string scriptFormat = @"window.openid_visible_iframe = {0}; // causes the hidden iframe to show up window.openid_trace = {1}; // causes lots of messages"; - result.WriteScriptBlock(string.Format( - CultureInfo.InvariantCulture, - scriptFormat, - additionalOptions.ShowDiagnosticIFrame ? "true" : "false", - additionalOptions.ShowDiagnosticTrace ? "true" : "false")); - } - var scriptResources = new[] { + result.WriteScriptBlock(string.Format( + CultureInfo.InvariantCulture, + scriptFormat, + additionalOptions.ShowDiagnosticIFrame ? "true" : "false", + additionalOptions.ShowDiagnosticTrace ? "true" : "false")); + } + var scriptResources = new[] { OpenIdRelyingPartyControlBase.EmbeddedJavascriptResource, OpenIdRelyingPartyAjaxControlBase.EmbeddedAjaxJavascriptResource, OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedScriptResourceName, }; - result.WriteScriptTags(scriptResources); + result.WriteScriptTags(scriptResources); - if (selectorOptions.DownloadYahooUILibrary) { - result.WriteScriptTagsUrls(new[] { "https://ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/yuiloader/yuiloader-min.js" }); - } + if (selectorOptions.DownloadYahooUILibrary) { + result.WriteScriptTagsUrls(new[] { "https://ajax.googleapis.com/ajax/libs/yui/2.8.0r4/build/yuiloader/yuiloader-min.js" }); + } - var blockBuilder = new StringWriter(); - if (selectorOptions.DownloadYahooUILibrary) { - blockBuilder.WriteLine(@" try { + using (var blockBuilder = new StringWriter(CultureInfo.CurrentCulture)) { + if (selectorOptions.DownloadYahooUILibrary) { + blockBuilder.WriteLine(@" try { if (YAHOO) { var loader = new YAHOO.util.YUILoader({ require: ['button', 'menu'], @@ -106,25 +110,25 @@ window.openid_trace = {1}; // causes lots of messages"; loader.insert(); } } catch (e) { }"); - } + } - blockBuilder.WriteLine("window.aspnetapppath = '{0}';", VirtualPathUtility.AppendTrailingSlash(HttpContext.Current.Request.ApplicationPath)); + blockBuilder.WriteLine("window.aspnetapppath = '{0}';", VirtualPathUtility.AppendTrailingSlash(HttpContext.Current.Request.ApplicationPath)); - // Positive assertions can last no longer than this library is willing to consider them valid, - // and when they come with OP private associations they last no longer than the OP is willing - // to consider them valid. We assume the OP will hold them valid for at least five minutes. - double assertionLifetimeInMilliseconds = Math.Min(TimeSpan.FromMinutes(5).TotalMilliseconds, Math.Min(DotNetOpenAuthSection.Configuration.OpenId.MaxAuthenticationTime.TotalMilliseconds, DotNetOpenAuthSection.Configuration.Messaging.MaximumMessageLifetime.TotalMilliseconds)); - blockBuilder.WriteLine( - "{0} = {1};", - OpenIdRelyingPartyAjaxControlBase.MaxPositiveAssertionLifetimeJsName, - assertionLifetimeInMilliseconds.ToString(CultureInfo.InvariantCulture)); + // Positive assertions can last no longer than this library is willing to consider them valid, + // and when they come with OP private associations they last no longer than the OP is willing + // to consider them valid. We assume the OP will hold them valid for at least five minutes. + double assertionLifetimeInMilliseconds = Math.Min(TimeSpan.FromMinutes(5).TotalMilliseconds, Math.Min(DotNetOpenAuthSection.Configuration.OpenId.MaxAuthenticationTime.TotalMilliseconds, DotNetOpenAuthSection.Configuration.Messaging.MaximumMessageLifetime.TotalMilliseconds)); + blockBuilder.WriteLine( + "{0} = {1};", + OpenIdRelyingPartyAjaxControlBase.MaxPositiveAssertionLifetimeJsName, + assertionLifetimeInMilliseconds.ToString(CultureInfo.InvariantCulture)); - if (additionalOptions.PreloadedDiscoveryResults != null) { - blockBuilder.WriteLine(additionalOptions.PreloadedDiscoveryResults); - } + if (additionalOptions.PreloadedDiscoveryResults != null) { + blockBuilder.WriteLine(additionalOptions.PreloadedDiscoveryResults); + } - string discoverUrl = VirtualPathUtility.AppendTrailingSlash(HttpContext.Current.Request.ApplicationPath) + html.RouteCollection["OpenIdDiscover"].GetVirtualPath(html.ViewContext.RequestContext, new RouteValueDictionary(new { identifier = "xxx" })).VirtualPath; - string blockFormat = @" {0} = function (argument, resultFunction, errorCallback) {{ + string discoverUrl = VirtualPathUtility.AppendTrailingSlash(HttpContext.Current.Request.ApplicationPath) + html.RouteCollection["OpenIdDiscover"].GetVirtualPath(html.ViewContext.RequestContext, new RouteValueDictionary(new { identifier = "xxx" })).VirtualPath; + string blockFormat = @" {0} = function (argument, resultFunction, errorCallback) {{ jQuery.ajax({{ async: true, dataType: 'text', @@ -133,22 +137,22 @@ window.openid_trace = {1}; // causes lots of messages"; url: '{1}'.replace('xxx', encodeURIComponent(argument)) }}); }};"; - blockBuilder.WriteLine(blockFormat, OpenIdRelyingPartyAjaxControlBase.CallbackJSFunctionAsync, discoverUrl); + blockBuilder.WriteLine(blockFormat, OpenIdRelyingPartyAjaxControlBase.CallbackJSFunctionAsync, discoverUrl); - blockFormat = @" window.postLoginAssertion = function (positiveAssertion) {{ + blockFormat = @" window.postLoginAssertion = function (positiveAssertion) {{ $('#{0}')[0].setAttribute('value', positiveAssertion); if ($('#{1}')[0] && !$('#{1}')[0].value) {{ // popups have no ReturnUrl predefined, but full page LogOn does. $('#{1}')[0].setAttribute('value', window.parent.location.href); }} document.forms[{2}].submit(); }};"; - blockBuilder.WriteLine( - blockFormat, - additionalOptions.AssertionHiddenFieldId, - additionalOptions.ReturnUrlHiddenFieldId, - additionalOptions.FormKey); + blockBuilder.WriteLine( + blockFormat, + additionalOptions.AssertionHiddenFieldId, + additionalOptions.ReturnUrlHiddenFieldId, + additionalOptions.FormKey); - blockFormat = @" $(function () {{ + blockFormat = @" $(function () {{ var box = document.getElementsByName('openid_identifier')[0]; initAjaxOpenId(box, {0}, {1}, {2}, {3}, {4}, {5}, null, // js function to invoke on receiving a positive assertion @@ -156,32 +160,41 @@ window.openid_trace = {1}; // causes lots of messages"; false, // auto postback null); // PostBackEventReference (unused in MVC) }});"; - blockBuilder.WriteLine( - blockFormat, - MessagingUtilities.GetSafeJavascriptValue(Util.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenIdTextBox.EmbeddedLogoResourceName)), - MessagingUtilities.GetSafeJavascriptValue(Util.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedSpinnerResourceName)), - MessagingUtilities.GetSafeJavascriptValue(Util.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginSuccessResourceName)), - MessagingUtilities.GetSafeJavascriptValue(Util.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginFailureResourceName)), - selectorOptions.Throttle, - selectorOptions.Timeout.TotalMilliseconds, - MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.LogOnText), - MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.LogOnToolTip), - selectorOptions.TextBox.ShowLogOnPostBackButton ? "true" : "false", - MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.LogOnPostBackToolTip), - MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.RetryText), - MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.RetryToolTip), - MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.BusyToolTip), - MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.IdentifierRequiredMessage), - MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.LogOnInProgressMessage), - MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.AuthenticationSucceededToolTip), - MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.AuthenticatedAsToolTip), - MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.AuthenticationFailedToolTip)); - - result.WriteScriptBlock(blockBuilder.ToString()); - result.WriteScriptTags(OpenId.RelyingParty.OpenIdSelector.EmbeddedScriptResourceName); - - Reporting.RecordFeatureUse("MVC " + typeof(OpenIdSelector).Name); - return result.ToString(); + blockBuilder.WriteLine( + blockFormat, + MessagingUtilities.GetSafeJavascriptValue(Util.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenIdTextBox.EmbeddedLogoResourceName)), + MessagingUtilities.GetSafeJavascriptValue(Util.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedSpinnerResourceName)), + MessagingUtilities.GetSafeJavascriptValue(Util.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginSuccessResourceName)), + MessagingUtilities.GetSafeJavascriptValue(Util.GetWebResourceUrl(typeof(OpenIdRelyingPartyControlBase), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginFailureResourceName)), + selectorOptions.Throttle, + selectorOptions.Timeout.TotalMilliseconds, + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.LogOnText), + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.LogOnToolTip), + selectorOptions.TextBox.ShowLogOnPostBackButton ? "true" : "false", + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.LogOnPostBackToolTip), + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.RetryText), + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.RetryToolTip), + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.BusyToolTip), + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.IdentifierRequiredMessage), + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.LogOnInProgressMessage), + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.AuthenticationSucceededToolTip), + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.AuthenticatedAsToolTip), + MessagingUtilities.GetSafeJavascriptValue(selectorOptions.TextBox.AuthenticationFailedToolTip)); + + result.WriteScriptBlock(blockBuilder.ToString()); + result.WriteScriptTags(OpenId.RelyingParty.OpenIdSelector.EmbeddedScriptResourceName); + + Reporting.RecordFeatureUse("MVC " + typeof(OpenIdSelector).Name); + return result.ToString(); + } + } + } catch { + if (selectorOptionsOwned) { + selectorOptions.Dispose(); + } + + throw; + } } /// <summary> @@ -227,40 +240,42 @@ window.openid_trace = {1}; // causes lots of messages"; /// <returns> /// HTML that should be sent directly to the browser. /// </returns> + [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "Not a problem for this type.")] public static string OpenIdSelector(this HtmlHelper html, params SelectorButton[] buttons) { Contract.Requires<ArgumentNullException>(html != null); Contract.Requires<ArgumentNullException>(buttons != null); Contract.Ensures(Contract.Result<string>() != null); - var writer = new StringWriter(); - var h = new HtmlTextWriter(writer); + using (var writer = new StringWriter(CultureInfo.CurrentCulture)) { + using (var h = new HtmlTextWriter(writer)) { + h.AddAttribute(HtmlTextWriterAttribute.Class, "OpenIdProviders"); + h.RenderBeginTag(HtmlTextWriterTag.Ul); - h.AddAttribute(HtmlTextWriterAttribute.Class, "OpenIdProviders"); - h.RenderBeginTag(HtmlTextWriterTag.Ul); + foreach (SelectorButton button in buttons) { + var op = button as SelectorProviderButton; + if (op != null) { + h.Write(OpenIdSelectorOPButton(html, op.OPIdentifier, op.Image)); + continue; + } - foreach (SelectorButton button in buttons) { - var op = button as SelectorProviderButton; - if (op != null) { - h.Write(OpenIdSelectorOPButton(html, op.OPIdentifier, op.Image)); - continue; - } + var openid = button as SelectorOpenIdButton; + if (openid != null) { + h.Write(OpenIdSelectorOpenIdButton(html, openid.Image)); + continue; + } - var openid = button as SelectorOpenIdButton; - if (openid != null) { - h.Write(OpenIdSelectorOpenIdButton(html, openid.Image)); - continue; - } + ErrorUtilities.VerifySupported(false, "The {0} button is not yet supported for MVC.", button.GetType().Name); + } - ErrorUtilities.VerifySupported(false, "The {0} button is not yet supported for MVC.", button.GetType().Name); - } + h.RenderEndTag(); // ul - h.RenderEndTag(); // ul + if (buttons.OfType<SelectorOpenIdButton>().Any()) { + h.Write(OpenIdAjaxTextBox(html)); + } + } - if (buttons.OfType<SelectorOpenIdButton>().Any()) { - h.Write(OpenIdAjaxTextBox(html)); + return writer.ToString(); } - - return writer.ToString(); } /// <summary> @@ -271,6 +286,7 @@ window.openid_trace = {1}; // causes lots of messages"; /// <returns> /// HTML that should be sent directly to the browser. /// </returns> + [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "html", Justification = "Breaking change, and it's an extension method so it's useful.")] public static string OpenIdAjaxTextBox(this HtmlHelper html) { return @"<div style='display: none' id='OpenIDForm'> <span class='OpenIdAjaxTextBox' style='display: inline-block; position: relative; font-size: 16px'> @@ -289,48 +305,50 @@ window.openid_trace = {1}; // causes lots of messages"; /// <returns> /// HTML that should be sent directly to the browser. /// </returns> + [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "Not a problem for this type.")] private static string OpenIdSelectorButton(this HtmlHelper html, string id, string cssClass, string imageUrl) { Contract.Requires<ArgumentNullException>(html != null); Contract.Requires<ArgumentNullException>(id != null); Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(imageUrl)); Contract.Ensures(Contract.Result<string>() != null); - var writer = new StringWriter(); - var h = new HtmlTextWriter(writer); - - h.AddAttribute(HtmlTextWriterAttribute.Id, id); - if (!string.IsNullOrEmpty(cssClass)) { - h.AddAttribute(HtmlTextWriterAttribute.Class, cssClass); - } - h.RenderBeginTag(HtmlTextWriterTag.Li); + using (var writer = new StringWriter(CultureInfo.CurrentCulture)) { + using (var h = new HtmlTextWriter(writer)) { + h.AddAttribute(HtmlTextWriterAttribute.Id, id); + if (!string.IsNullOrEmpty(cssClass)) { + h.AddAttribute(HtmlTextWriterAttribute.Class, cssClass); + } + h.RenderBeginTag(HtmlTextWriterTag.Li); - h.AddAttribute(HtmlTextWriterAttribute.Href, "#"); - h.RenderBeginTag(HtmlTextWriterTag.A); + h.AddAttribute(HtmlTextWriterAttribute.Href, "#"); + h.RenderBeginTag(HtmlTextWriterTag.A); - h.RenderBeginTag(HtmlTextWriterTag.Div); - h.RenderBeginTag(HtmlTextWriterTag.Div); + h.RenderBeginTag(HtmlTextWriterTag.Div); + h.RenderBeginTag(HtmlTextWriterTag.Div); - h.AddAttribute(HtmlTextWriterAttribute.Src, imageUrl); - h.RenderBeginTag(HtmlTextWriterTag.Img); - h.RenderEndTag(); + h.AddAttribute(HtmlTextWriterAttribute.Src, imageUrl); + h.RenderBeginTag(HtmlTextWriterTag.Img); + h.RenderEndTag(); - h.AddAttribute(HtmlTextWriterAttribute.Src, Util.GetWebResourceUrl(typeof(OpenIdSelector), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginSuccessResourceName)); - h.AddAttribute(HtmlTextWriterAttribute.Class, "loginSuccess"); - h.AddAttribute(HtmlTextWriterAttribute.Title, "Authenticated as {0}"); - h.RenderBeginTag(HtmlTextWriterTag.Img); - h.RenderEndTag(); + h.AddAttribute(HtmlTextWriterAttribute.Src, Util.GetWebResourceUrl(typeof(OpenIdSelector), OpenId.RelyingParty.OpenIdAjaxTextBox.EmbeddedLoginSuccessResourceName)); + h.AddAttribute(HtmlTextWriterAttribute.Class, "loginSuccess"); + h.AddAttribute(HtmlTextWriterAttribute.Title, "Authenticated as {0}"); + h.RenderBeginTag(HtmlTextWriterTag.Img); + h.RenderEndTag(); - h.RenderEndTag(); // div + h.RenderEndTag(); // div - h.AddAttribute(HtmlTextWriterAttribute.Class, "ui-widget-overlay"); - h.RenderBeginTag(HtmlTextWriterTag.Div); - h.RenderEndTag(); // div + h.AddAttribute(HtmlTextWriterAttribute.Class, "ui-widget-overlay"); + h.RenderBeginTag(HtmlTextWriterTag.Div); + h.RenderEndTag(); // div - h.RenderEndTag(); // div - h.RenderEndTag(); // a - h.RenderEndTag(); // li + h.RenderEndTag(); // div + h.RenderEndTag(); // a + h.RenderEndTag(); // li + } - return writer.ToString(); + return writer.ToString(); + } } /// <summary> diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/HmacSha1SigningBindingElement.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/HmacSha1SigningBindingElement.cs index 53930bc..5828428 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/HmacSha1SigningBindingElement.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/HmacSha1SigningBindingElement.cs @@ -32,10 +32,11 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// </remarks> protected override string GetSignature(ITamperResistantOAuthMessage message) { string key = GetConsumerAndTokenSecretString(message); - HashAlgorithm hasher = new HMACSHA1(Encoding.ASCII.GetBytes(key)); - string baseString = ConstructSignatureBaseString(message, this.Channel.MessageDescriptions.GetAccessor(message)); - byte[] digest = hasher.ComputeHash(Encoding.ASCII.GetBytes(baseString)); - return Convert.ToBase64String(digest); + using (HashAlgorithm hasher = new HMACSHA1(Encoding.ASCII.GetBytes(key))) { + string baseString = ConstructSignatureBaseString(message, this.Channel.MessageDescriptions.GetAccessor(message)); + byte[] digest = hasher.ComputeHash(Encoding.ASCII.GetBytes(baseString)); + return Convert.ToBase64String(digest); + } } /// <summary> diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/ITamperResistantOAuthMessage.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/ITamperResistantOAuthMessage.cs index ff6d6e9..a95001d 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/ITamperResistantOAuthMessage.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/ITamperResistantOAuthMessage.cs @@ -13,7 +13,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// <summary> /// An interface that OAuth messages implement to support signing. /// </summary> - public interface ITamperResistantOAuthMessage : IDirectedProtocolMessage, ITamperResistantProtocolMessage { + public interface ITamperResistantOAuthMessage : IDirectedProtocolMessage, ITamperResistantProtocolMessage, IMessageOriginalPayload { /// <summary> /// Gets or sets the method used to sign the message. /// </summary> diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs index e6cfb78..2b1bea5 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { using System; using System.Collections.Generic; using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Globalization; using System.IO; @@ -32,6 +33,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// <param name="store">The web application store to use for nonces.</param> /// <param name="tokenManager">The token manager instance to use.</param> /// <param name="securitySettings">The security settings.</param> + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.Contracts.__ContractsRuntime.Requires<System.ArgumentNullException>(System.Boolean,System.String,System.String)", Justification = "Code contracts"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "securitySettings", Justification = "Code contracts")] internal OAuthChannel(ITamperProtectionChannelBindingElement signingBindingElement, INonceStore store, IConsumerTokenManager tokenManager, ConsumerSecuritySettings securitySettings) : this( signingBindingElement, @@ -52,6 +54,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// <param name="store">The web application store to use for nonces.</param> /// <param name="tokenManager">The token manager instance to use.</param> /// <param name="securitySettings">The security settings.</param> + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.Contracts.__ContractsRuntime.Requires<System.ArgumentNullException>(System.Boolean,System.String,System.String)", Justification = "Code contracts"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "securitySettings", Justification = "Code contracts")] internal OAuthChannel(ITamperProtectionChannelBindingElement signingBindingElement, INonceStore store, IServiceProviderTokenManager tokenManager, ServiceProviderSecuritySettings securitySettings) : this( signingBindingElement, @@ -75,6 +78,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// <param name="messageTypeProvider">An injected message type provider instance. /// Except for mock testing, this should always be one of /// <see cref="OAuthConsumerMessageFactory"/> or <see cref="OAuthServiceProviderMessageFactory"/>.</param> + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.Contracts.__ContractsRuntime.Requires<System.ArgumentNullException>(System.Boolean,System.String,System.String)", Justification = "Code contracts"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "securitySettings", Justification = "Code contracts")] internal OAuthChannel(ITamperProtectionChannelBindingElement signingBindingElement, INonceStore store, ITokenManager tokenManager, SecuritySettings securitySettings, IMessageFactory messageTypeProvider) : base(messageTypeProvider, InitializeBindingElements(signingBindingElement, store, tokenManager, securitySettings)) { Contract.Requires<ArgumentNullException>(tokenManager != null); @@ -292,8 +296,9 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { }; var spTokenManager = tokenManager as IServiceProviderTokenManager; - if (spTokenManager != null) { - bindingElements.Insert(0, new TokenHandlingBindingElement(spTokenManager, (ServiceProviderSecuritySettings)securitySettings)); + var serviceProviderSecuritySettings = securitySettings as ServiceProviderSecuritySettings; + if (spTokenManager != null && serviceProviderSecuritySettings != null) { + bindingElements.Insert(0, new TokenHandlingBindingElement(spTokenManager, serviceProviderSecuritySettings)); } return bindingElements.ToArray(); diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs index cb81139..31b5149 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { using System; using System.Collections.Generic; using System.Collections.Specialized; + using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Globalization; using System.Linq; @@ -148,6 +149,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// <remarks> /// This method implements OAuth 1.0 section 9.1. /// </remarks> + [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Unavoidable")] internal static string ConstructSignatureBaseString(ITamperResistantOAuthMessage message, MessageDictionary messageDictionary) { Contract.Requires<ArgumentNullException>(message != null); Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(message.HttpMethod)); @@ -175,6 +177,13 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { partsToInclude = messageDictionary; } + // If this message was deserialized, include only those explicitly included message parts (excludes defaulted values) + // in the signature. + var originalPayloadMessage = (IMessageOriginalPayload)message; + if (originalPayloadMessage.OriginalPayload != null) { + partsToInclude = partsToInclude.Where(pair => originalPayloadMessage.OriginalPayload.ContainsKey(pair.Key)); + } + foreach (var pair in OAuthChannel.GetUriEscapedParameters(partsToInclude)) { encodedDictionary[pair.Key] = pair.Value; } diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs index 329f8c4..bfebd8b 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs @@ -7,6 +7,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { using System; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Linq; using System.Text; @@ -34,6 +35,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// </summary> /// <param name="tokenManager">The token manager.</param> /// <param name="securitySettings">The security settings.</param> + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.Contracts.__ContractsRuntime.Requires<System.ArgumentNullException>(System.Boolean,System.String,System.String)", Justification = "Code contract"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "securitySettings", Justification = "Code contracts")] internal TokenHandlingBindingElement(IServiceProviderTokenManager tokenManager, ServiceProviderSecuritySettings securitySettings) { Contract.Requires<ArgumentNullException>(tokenManager != null); Contract.Requires<ArgumentNullException>(securitySettings != null, "securitySettings"); diff --git a/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs b/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs index 57ce470..6084fc0 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs @@ -6,6 +6,7 @@ namespace DotNetOpenAuth.OAuth.Messages { using System; + using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; @@ -131,6 +132,24 @@ namespace DotNetOpenAuth.OAuth.Messages { #endregion + #region IMessageOriginalPayload Members + + /// <summary> + /// Gets or sets the original message parts, before any normalization or default values were assigned. + /// </summary> + IDictionary<string, string> IMessageOriginalPayload.OriginalPayload { + get { return this.OriginalPayload; } + set { this.OriginalPayload = value; } + } + + /// <summary> + /// Gets or sets the original message parts, before any normalization or default values were assigned. + /// </summary> + [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "By design")] + protected IDictionary<string, string> OriginalPayload { get; set; } + + #endregion + /// <summary> /// Gets or sets the signature method used to sign the request. /// </summary> diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs index fb4c9a1..723839d 100644 --- a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs +++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:4.0.30104.0 +// Runtime Version:4.0.30319.225 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/src/DotNetOpenAuth/OAuth/Protocol.cs b/src/DotNetOpenAuth/OAuth/Protocol.cs index a524ba7..6eecd8e 100644 --- a/src/DotNetOpenAuth/OAuth/Protocol.cs +++ b/src/DotNetOpenAuth/OAuth/Protocol.cs @@ -83,7 +83,7 @@ namespace DotNetOpenAuth.OAuth { internal static readonly List<Protocol> AllVersions = new List<Protocol>() { V10a, V10 }; /// <summary> - /// The default (or most recent) supported version of the OpenID protocol. + /// The default (or most recent) supported version of the OAuth protocol. /// </summary> internal static readonly Protocol Default = AllVersions[0]; diff --git a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs index fda895e..ec9206e 100644 --- a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs +++ b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs @@ -341,7 +341,7 @@ namespace DotNetOpenAuth.OAuth { Contract.Requires<ArgumentException>((consumerKey == null) == (scope == null)); Contract.Requires<InvalidOperationException>(this.TokenManager is ICombinedOpenIdProviderTokenManager); var openidTokenManager = (ICombinedOpenIdProviderTokenManager)this.TokenManager; - ErrorUtilities.VerifyArgument(consumerKey == null || consumerKey == openidTokenManager.GetConsumerKey(openIdAuthenticationRequest.Realm), "The consumer key and the realm did not match according to the token manager."); + ErrorUtilities.VerifyArgument(consumerKey == null || consumerKey == openidTokenManager.GetConsumerKey(openIdAuthenticationRequest.Realm), OAuthStrings.OpenIdOAuthRealmConsumerKeyDoNotMatch); this.AttachAuthorizationResponse(openIdAuthenticationRequest, scope); } diff --git a/src/DotNetOpenAuth/OAuth/VerificationCodeFormat.cs b/src/DotNetOpenAuth/OAuth/VerificationCodeFormat.cs index 3afd44e..a6560d8 100644 --- a/src/DotNetOpenAuth/OAuth/VerificationCodeFormat.cs +++ b/src/DotNetOpenAuth/OAuth/VerificationCodeFormat.cs @@ -37,6 +37,7 @@ namespace DotNetOpenAuth.OAuth { /// </remarks> [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Alikes", Justification = "Breaking change of existing API")] [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "AlphaNumeric", Justification = "Breaking change of existing API")] + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "LookAlikes", Justification = "Breaking change of existing API")] AlphaNumericNoLookAlikes, /// <summary> diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/KeyValueFormEncoding.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/KeyValueFormEncoding.cs index c363c11..46c2139 100644 --- a/src/DotNetOpenAuth/OpenId/ChannelElements/KeyValueFormEncoding.cs +++ b/src/DotNetOpenAuth/OpenId/ChannelElements/KeyValueFormEncoding.cs @@ -99,27 +99,30 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// If ordering of the key=value pairs is important, a deterministic enumerator must /// be used. /// </remarks> + [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "Not a problem for this type.")] public static byte[] GetBytes(IEnumerable<KeyValuePair<string, string>> keysAndValues) { Contract.Requires<ArgumentNullException>(keysAndValues != null); - MemoryStream ms = new MemoryStream(); - using (StreamWriter sw = new StreamWriter(ms, textEncoding)) { - sw.NewLine = NewLineCharacters; - foreach (var pair in keysAndValues) { - if (pair.Key.IndexOfAny(IllegalKeyCharacters) >= 0) { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, OpenIdStrings.InvalidCharacterInKeyValueFormInput, pair.Key)); - } - if (pair.Value.IndexOfAny(IllegalValueCharacters) >= 0) { - throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, OpenIdStrings.InvalidCharacterInKeyValueFormInput, pair.Value)); - } + using (MemoryStream ms = new MemoryStream()) { + using (StreamWriter sw = new StreamWriter(ms, textEncoding)) { + sw.NewLine = NewLineCharacters; + foreach (var pair in keysAndValues) { + if (pair.Key.IndexOfAny(IllegalKeyCharacters) >= 0) { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, OpenIdStrings.InvalidCharacterInKeyValueFormInput, pair.Key)); + } + if (pair.Value.IndexOfAny(IllegalValueCharacters) >= 0) { + throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, OpenIdStrings.InvalidCharacterInKeyValueFormInput, pair.Value)); + } - sw.Write(pair.Key); - sw.Write(':'); - sw.Write(pair.Value); - sw.WriteLine(); + sw.Write(pair.Key); + sw.Write(':'); + sw.Write(pair.Value); + sw.WriteLine(); + } } + + return ms.ToArray(); } - return ms.ToArray(); } /// <summary> diff --git a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs index 55c2dc5..df36b5e 100644 --- a/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs +++ b/src/DotNetOpenAuth/OpenId/Extensions/UI/UIRequest.cs @@ -31,7 +31,7 @@ namespace DotNetOpenAuth.OpenId.Extensions.UI { /// <see cref="IdentifierDiscoveryResult.IsExtensionSupported<T>()"/> method.</para> /// </remarks> [Serializable] - public sealed class UIRequest : IOpenIdMessageExtension, IMessageWithEvents { + public class UIRequest : IOpenIdMessageExtension, IMessageWithEvents { /// <summary> /// The factory method that may be used in deserialization of this message. /// </summary> diff --git a/src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs b/src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs index ba9852e..215ea24 100644 --- a/src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs +++ b/src/DotNetOpenAuth/OpenId/HostMetaDiscoveryService.cs @@ -212,6 +212,7 @@ namespace DotNetOpenAuth.OpenId { /// <param name="response">The response.</param> /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param> /// <exception cref="ProtocolException">Thrown if the XRDS document has an invalid or a missing signature.</exception> + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "XmlDSig", Justification = "xml")] private static void ValidateXmlDSig(XrdsDocument document, UriIdentifier identifier, IncomingWebResponse response, string signingHost) { Contract.Requires<ArgumentNullException>(document != null); Contract.Requires<ArgumentNullException>(identifier != null); @@ -223,10 +224,10 @@ namespace DotNetOpenAuth.OpenId { ErrorUtilities.VerifyProtocol(signedInfoNode != null, OpenIdStrings.MissingElement, "SignedInfo"); ErrorUtilities.VerifyProtocol( signedInfoNode.SelectSingleNode("ds:CanonicalizationMethod[@Algorithm='http://docs.oasis-open.org/xri/xrd/2009/01#canonicalize-raw-octets']", document.XmlNamespaceResolver) != null, - "Unrecognized or missing canonicalization method."); + OpenIdStrings.UnsupportedCanonicalizationMethod); ErrorUtilities.VerifyProtocol( signedInfoNode.SelectSingleNode("ds:SignatureMethod[@Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1']", document.XmlNamespaceResolver) != null, - "Unrecognized or missing signature method."); + OpenIdStrings.UnsupportedSignatureMethod); var certNodes = signatureNode.Select("ds:KeyInfo/ds:X509Data/ds:X509Certificate", document.XmlNamespaceResolver); ErrorUtilities.VerifyProtocol(certNodes.Count > 0, OpenIdStrings.MissingElement, "X509Certificate"); var certs = certNodes.Cast<XPathNavigator>().Select(n => new X509Certificate2(Convert.FromBase64String(n.Value.Trim()))).ToList(); @@ -249,7 +250,7 @@ namespace DotNetOpenAuth.OpenId { // Verify that the certificate is issued to the host on whom we are performing discovery. string hostName = certs[0].GetNameInfo(X509NameType.DnsName, false); - ErrorUtilities.VerifyProtocol(string.Equals(hostName, signingHost, StringComparison.OrdinalIgnoreCase), "X.509 signing certificate issued to {0}, but a certificate for {1} was expected.", hostName, signingHost); + ErrorUtilities.VerifyProtocol(string.Equals(hostName, signingHost, StringComparison.OrdinalIgnoreCase), OpenIdStrings.MisdirectedSigningCertificate, hostName, signingHost); // Verify the signature itself byte[] signature = Convert.FromBase64String(response.Headers["Signature"]); @@ -257,7 +258,7 @@ namespace DotNetOpenAuth.OpenId { byte[] data = new byte[response.ResponseStream.Length]; response.ResponseStream.Seek(0, SeekOrigin.Begin); response.ResponseStream.Read(data, 0, data.Length); - ErrorUtilities.VerifyProtocol(provider.VerifyData(data, "SHA1", signature), "Invalid XmlDSig signature on XRDS document."); + ErrorUtilities.VerifyProtocol(provider.VerifyData(data, "SHA1", signature), OpenIdStrings.InvalidDSig); } /// <summary> @@ -271,7 +272,7 @@ namespace DotNetOpenAuth.OpenId { /// an alternative plan. /// </remarks> /// <exception cref="ProtocolException">Thrown if the certificate chain is invalid or unverifiable.</exception> - [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "By design")] + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "DotNetOpenAuth.Messaging.ErrorUtilities.ThrowProtocol(System.String,System.Object[])", Justification = "The localized portion is a string resource already."), SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "By design")] private static void VerifyCertChain(List<X509Certificate2> certs) { var chain = new X509Chain(); foreach (var cert in certs) { diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateDiffieHellmanResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateDiffieHellmanResponse.cs index ccfb1b8..a4fdf49 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/AssociateDiffieHellmanResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateDiffieHellmanResponse.cs @@ -53,7 +53,7 @@ namespace DotNetOpenAuth.OpenId.Messages { /// </remarks> protected override Association CreateAssociationAtRelyingParty(AssociateRequest request) { var diffieHellmanRequest = request as AssociateDiffieHellmanRequest; - ErrorUtilities.VerifyArgument(diffieHellmanRequest != null, "request"); + ErrorUtilities.VerifyArgument(diffieHellmanRequest != null, OpenIdStrings.DiffieHellmanAssociationRequired); HashAlgorithm hasher = DiffieHellmanUtilities.Lookup(Protocol, this.SessionType); byte[] associationSecret = DiffieHellmanUtilities.SHAHashXorSecret(hasher, diffieHellmanRequest.Algorithm, this.DiffieHellmanServerPublic, this.EncodedMacKey); @@ -86,14 +86,14 @@ namespace DotNetOpenAuth.OpenId.Messages { // that will be transmitted to the Relying Party. The RP will perform an inverse operation // using its part of a DH secret in order to decrypt the shared secret we just invented // above when we created the association. - DiffieHellman dh = new DiffieHellmanManaged( + using (DiffieHellman dh = new DiffieHellmanManaged( diffieHellmanRequest.DiffieHellmanModulus ?? AssociateDiffieHellmanRequest.DefaultMod, diffieHellmanRequest.DiffieHellmanGen ?? AssociateDiffieHellmanRequest.DefaultGen, - AssociateDiffieHellmanRequest.DefaultX); - HashAlgorithm hasher = DiffieHellmanUtilities.Lookup(this.Protocol, this.SessionType); - this.DiffieHellmanServerPublic = DiffieHellmanUtilities.EnsurePositive(dh.CreateKeyExchange()); - this.EncodedMacKey = DiffieHellmanUtilities.SHAHashXorSecret(hasher, dh, diffieHellmanRequest.DiffieHellmanConsumerPublic, association.SecretKey); - + AssociateDiffieHellmanRequest.DefaultX)) { + HashAlgorithm hasher = DiffieHellmanUtilities.Lookup(this.Protocol, this.SessionType); + this.DiffieHellmanServerPublic = DiffieHellmanUtilities.EnsurePositive(dh.CreateKeyExchange()); + this.EncodedMacKey = DiffieHellmanUtilities.SHAHashXorSecret(hasher, dh, diffieHellmanRequest.DiffieHellmanConsumerPublic, association.SecretKey); + } return association; } } diff --git a/src/DotNetOpenAuth/OpenId/Messages/IndirectResponseBase.cs b/src/DotNetOpenAuth/OpenId/Messages/IndirectResponseBase.cs index d53b9d0..fce6028 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/IndirectResponseBase.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/IndirectResponseBase.cs @@ -16,7 +16,12 @@ namespace DotNetOpenAuth.OpenId.Messages { /// A common base class from which indirect response messages should derive. /// </summary> [Serializable] - internal class IndirectResponseBase : RequestBase { + internal class IndirectResponseBase : RequestBase, IProtocolMessageWithExtensions { + /// <summary> + /// Backing store for the <see cref="Extensions"/> property. + /// </summary> + private IList<IExtensionMessage> extensions = new List<IExtensionMessage>(); + /// <summary> /// Initializes a new instance of the <see cref="IndirectResponseBase"/> class. /// </summary> @@ -42,6 +47,35 @@ namespace DotNetOpenAuth.OpenId.Messages { : base(version, relyingPartyReturnTo, mode, MessageTransport.Indirect) { } + #region IProtocolMessageWithExtensions Members + + /// <summary> + /// Gets the list of extensions that are included with this message. + /// </summary> + /// <value></value> + /// <remarks> + /// Implementations of this interface should ensure that this property never returns null. + /// </remarks> + public IList<IExtensionMessage> Extensions { + get { return this.extensions; } + } + + #endregion + + /// <summary> + /// Gets the signed extensions on this message. + /// </summary> + internal IEnumerable<IOpenIdMessageExtension> SignedExtensions { + get { return this.extensions.OfType<IOpenIdMessageExtension>().Where(ext => ext.IsSignedByRemoteParty); } + } + + /// <summary> + /// Gets the unsigned extensions on this message. + /// </summary> + internal IEnumerable<IOpenIdMessageExtension> UnsignedExtensions { + get { return this.extensions.OfType<IOpenIdMessageExtension>().Where(ext => !ext.IsSignedByRemoteParty); } + } + /// <summary> /// Gets the originating request message, if applicable. /// </summary> diff --git a/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs index 776b28b..baeae16 100644 --- a/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs +++ b/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs @@ -26,7 +26,7 @@ namespace DotNetOpenAuth.OpenId.Messages { /// </summary> [DebuggerDisplay("OpenID {Version} {Mode} (no id assertion)")] [Serializable] - internal class IndirectSignedResponse : IndirectResponseBase, ITamperResistantOpenIdMessage, IProtocolMessageWithExtensions { + internal class IndirectSignedResponse : IndirectResponseBase, ITamperResistantOpenIdMessage { /// <summary> /// The allowed date/time formats for the response_nonce parameter. /// </summary> @@ -36,11 +36,6 @@ namespace DotNetOpenAuth.OpenId.Messages { private static readonly string[] PermissibleDateTimeFormats = { "yyyy-MM-ddTHH:mm:ssZ" }; /// <summary> - /// Backing store for the <see cref="Extensions"/> property. - /// </summary> - private IList<IExtensionMessage> extensions = new List<IExtensionMessage>(); - - /// <summary> /// Backing field for the <see cref="IExpiringProtocolMessage.UtcCreationDate"/> property. /// </summary> /// <remarks> @@ -104,21 +99,6 @@ namespace DotNetOpenAuth.OpenId.Messages { this.ReturnTo = relyingPartyReturnTo; } - #region IProtocolMessageWithExtensions Members - - /// <summary> - /// Gets the list of extensions that are included with this message. - /// </summary> - /// <value></value> - /// <remarks> - /// Implementations of this interface should ensure that this property never returns null. - /// </remarks> - public IList<IExtensionMessage> Extensions { - get { return this.extensions; } - } - - #endregion - /// <summary> /// Gets the level of protection this message requires. /// </summary> @@ -241,20 +221,6 @@ namespace DotNetOpenAuth.OpenId.Messages { internal bool ReturnToParametersSignatureValidated { get; set; } /// <summary> - /// Gets the signed extensions on this message. - /// </summary> - internal IEnumerable<IOpenIdMessageExtension> SignedExtensions { - get { return this.extensions.OfType<IOpenIdMessageExtension>().Where(ext => ext.IsSignedByRemoteParty); } - } - - /// <summary> - /// Gets the unsigned extensions on this message. - /// </summary> - internal IEnumerable<IOpenIdMessageExtension> UnsignedExtensions { - get { return this.extensions.OfType<IOpenIdMessageExtension>().Where(ext => !ext.IsSignedByRemoteParty); } - } - - /// <summary> /// Gets or sets the nonce that will protect the message from replay attacks. /// </summary> /// <value> diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs index 43283ac..f45af93 100644 --- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs +++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:4.0.30319.1 +// Runtime Version:4.0.30319.225 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -250,6 +250,15 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> + /// Looks up a localized string similar to The associate request instance must be a Diffie-Hellman instance.. + /// </summary> + internal static string DiffieHellmanAssociationRequired { + get { + return ResourceManager.GetString("DiffieHellmanAssociationRequired", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to The following properties must be set before the Diffie-Hellman algorithm can generate a public key: {0}. /// </summary> internal static string DiffieHellmanRequiredPropertiesNotSet { @@ -349,6 +358,15 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> + /// Looks up a localized string similar to Invalid XmlDSig signature on XRDS document.. + /// </summary> + internal static string InvalidDSig { + get { + return ResourceManager.GetString("InvalidDSig", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to Cannot decode Key-Value Form because a line was found without a '{0}' character. (line {1}: '{2}'). /// </summary> internal static string InvalidKeyValueFormCharacterMissing { @@ -425,6 +443,15 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> + /// Looks up a localized string similar to X.509 signing certificate issued to {0}, but a certificate for {1} was expected.. + /// </summary> + internal static string MisdirectedSigningCertificate { + get { + return ResourceManager.GetString("MisdirectedSigningCertificate", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to Missing {0} element.. /// </summary> internal static string MissingElement { @@ -524,11 +551,20 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> - /// Looks up a localized string similar to An positive OpenID assertion was received from OP endpoint {0} that is not on this relying party's whitelist.. + /// Looks up a localized string similar to OpenID popup window or iframe did not recognize an OpenID response in the request.. /// </summary> - internal static string PositiveAssertionFromNonWhitelistedProvider { + internal static string PopupRedirectMissingResponse { get { - return ResourceManager.GetString("PositiveAssertionFromNonWhitelistedProvider", resourceCulture); + return ResourceManager.GetString("PopupRedirectMissingResponse", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to An positive OpenID assertion was received from OP endpoint {0} and was rejected based on this site's security settings.. + /// </summary> + internal static string PositiveAssertionFromNonQualifiedProvider { + get { + return ResourceManager.GetString("PositiveAssertionFromNonQualifiedProvider", resourceCulture); } } @@ -722,6 +758,15 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> + /// Looks up a localized string similar to Unrecognized or missing canonicalization method.. + /// </summary> + internal static string UnsupportedCanonicalizationMethod { + get { + return ResourceManager.GetString("UnsupportedCanonicalizationMethod", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to This feature is unavailable due to an unrecognized channel configuration.. /// </summary> internal static string UnsupportedChannelConfiguration { @@ -731,6 +776,15 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> + /// Looks up a localized string similar to Unrecognized or missing signature method.. + /// </summary> + internal static string UnsupportedSignatureMethod { + get { + return ResourceManager.GetString("UnsupportedSignatureMethod", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to The openid.user_setup_url parameter is required when sending negative assertion messages in response to immediate mode requests.. /// </summary> internal static string UserSetupUrlRequiredInImmediateNegativeResponse { diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx index fab03a9..b700d76 100644 --- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx +++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx @@ -337,8 +337,8 @@ Discovered endpoint info: <data name="BadExtension" xml:space="preserve"> <value>The {0} extension failed to deserialize and will be skipped. {1}</value> </data> - <data name="PositiveAssertionFromNonWhitelistedProvider" xml:space="preserve"> - <value>An positive OpenID assertion was received from OP endpoint {0} that is not on this relying party's whitelist.</value> + <data name="PositiveAssertionFromNonQualifiedProvider" xml:space="preserve"> + <value>An positive OpenID assertion was received from OP endpoint {0} and was rejected based on this site's security settings.</value> </data> <data name="HeadTagMustIncludeRunatServer" xml:space="preserve"> <value>The HTML head tag must include runat="server".</value> @@ -355,4 +355,22 @@ Discovered endpoint info: <data name="MissingElement" xml:space="preserve"> <value>Missing {0} element.</value> </data> + <data name="DiffieHellmanAssociationRequired" xml:space="preserve"> + <value>The associate request instance must be a Diffie-Hellman instance.</value> + </data> + <data name="InvalidDSig" xml:space="preserve"> + <value>Invalid XmlDSig signature on XRDS document.</value> + </data> + <data name="MisdirectedSigningCertificate" xml:space="preserve"> + <value>X.509 signing certificate issued to {0}, but a certificate for {1} was expected.</value> + </data> + <data name="PopupRedirectMissingResponse" xml:space="preserve"> + <value>OpenID popup window or iframe did not recognize an OpenID response in the request.</value> + </data> + <data name="UnsupportedCanonicalizationMethod" xml:space="preserve"> + <value>Unrecognized or missing canonicalization method.</value> + </data> + <data name="UnsupportedSignatureMethod" xml:space="preserve"> + <value>Unrecognized or missing signature method.</value> + </data> </root>
\ No newline at end of file diff --git a/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequest.cs index ad73269..c5e6bba 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequest.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequest.cs @@ -27,7 +27,7 @@ namespace DotNetOpenAuth.OpenId.Provider { /// </summary> /// <param name="provider">The provider that received the request.</param> /// <param name="request">The incoming authentication request message.</param> - [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Code contracts require it.")] + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.Contracts.__ContractsRuntime.Requires<System.ArgumentException>(System.Boolean,System.String,System.String)", Justification = "Code contracts"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "AuthenticationRequest", Justification = "Type name"), SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Code contracts require it.")] internal AnonymousRequest(OpenIdProvider provider, SignedResponseRequest request) : base(provider, request) { Contract.Requires<ArgumentNullException>(provider != null); diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs index 967ef60..3a17263 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs @@ -501,14 +501,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { Contract.Requires<ArgumentNullException>(endpoints != null); Contract.Requires<ArgumentNullException>(relyingParty != null); - // Construct the endpoints filters based on criteria given by the host web site. - EndpointSelector versionFilter = ep => ep.Version >= Protocol.Lookup(relyingParty.SecuritySettings.MinimumRequiredOpenIdVersion).Version; - EndpointSelector hostingSiteFilter = relyingParty.EndpointFilter ?? (ep => true); - bool anyFilteredOut = false; var filteredEndpoints = new List<IdentifierDiscoveryResult>(); foreach (var endpoint in endpoints) { - if (versionFilter(endpoint) && hostingSiteFilter(endpoint)) { + if (relyingParty.FilterEndpoint(endpoint)) { filteredEndpoints.Add(endpoint); } else { anyFilteredOut = true; diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs index 869a342..9e3824d 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { using System; using System.Collections.Generic; using System.Diagnostics.Contracts; + using System.Linq; using System.Web; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.Messages; @@ -279,7 +280,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// have not been tampered with since the Provider sent the message.</para> /// </remarks> public T GetUntrustedExtension<T>() where T : IOpenIdMessageExtension { - return default(T); + return this.response.Extensions.OfType<T>().FirstOrDefault(); } /// <summary> @@ -303,7 +304,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// have not been tampered with since the Provider sent the message.</para> /// </remarks> public IOpenIdMessageExtension GetUntrustedExtension(Type extensionType) { - return null; + return this.response.Extensions.OfType<IOpenIdMessageExtension>().Where(ext => extensionType.IsInstanceOfType(ext)).FirstOrDefault(); } #endregion diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs index d80bf6a..8be097f 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs @@ -721,11 +721,16 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } var css = new HtmlLink(); - css.Href = this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedStylesheetResourceName); - css.Attributes["rel"] = "stylesheet"; - css.Attributes["type"] = "text/css"; - ErrorUtilities.VerifyHost(this.Page.Header != null, OpenIdStrings.HeadTagMustIncludeRunatServer); - this.Page.Header.Controls.AddAt(0, css); // insert at top so host page can override + try { + css.Href = this.Page.ClientScript.GetWebResourceUrl(this.GetType(), EmbeddedStylesheetResourceName); + css.Attributes["rel"] = "stylesheet"; + css.Attributes["type"] = "text/css"; + ErrorUtilities.VerifyHost(this.Page.Header != null, OpenIdStrings.HeadTagMustIncludeRunatServer); + this.Page.Header.Controls.AddAt(0, css); // insert at top so host page can override + } catch { + css.Dispose(); + throw; + } this.PrepareClientJavascript(); diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdButton.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdButton.cs index dbf6944..6243917 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdButton.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdButton.cs @@ -141,6 +141,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// Sends server control content to a provided <see cref="T:System.Web.UI.HtmlTextWriter"/> object, which writes the content to be rendered on the client. /// </summary> /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the server control content.</param> + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Web.UI.HtmlTextWriter.WriteEncodedText(System.String)", Justification = "Not localizable")] protected override void Render(HtmlTextWriter writer) { if (string.IsNullOrEmpty(this.Identifier)) { writer.WriteEncodedText(string.Format(CultureInfo.CurrentCulture, "[{0}]", OpenIdStrings.NoIdentifierSet)); diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdLogin.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdLogin.cs index 5128599..eccdacf 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdLogin.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdLogin.cs @@ -316,7 +316,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// Gets or sets the text to display if the user attempts to login /// without providing an Identifier. /// </summary> - [Bindable(true)] + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "br", Justification = "HTML"), Bindable(true)] [Category("Appearance")] [DefaultValue(RequiredTextDefault)] [Localizable(true)] @@ -336,7 +336,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <summary> /// Gets or sets the text to display if the user provides an invalid form for an Identifier. /// </summary> - [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "Property grid only supports primitive types.")] + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "br", Justification = "HTML"), SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "Property grid only supports primitive types.")] [Bindable(true)] [Category("Appearance")] [DefaultValue(UriFormatTextDefault)] @@ -687,150 +687,156 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <summary> /// Initializes the child controls. /// </summary> + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Web.UI.WebControls.WebControl.set_ToolTip(System.String)", Justification = "By design"), SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Web.UI.WebControls.Label.set_Text(System.String)", Justification = "By design"), SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Web.UI.WebControls.HyperLink.set_Text(System.String)", Justification = "By design"), SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Web.UI.WebControls.CheckBox.set_Text(System.String)", Justification = "By design"), SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Web.UI.WebControls.Button.set_Text(System.String)", Justification = "By design"), SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Web.UI.WebControls.BaseValidator.set_ErrorMessage(System.String)", Justification = "By design"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "br", Justification = "HTML"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "OpenID", Justification = "It is correct"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "MyOpenID", Justification = "Correct spelling"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "myopenid", Justification = "URL")] protected void InitializeControls() { this.panel = new Panel(); Table table = new Table(); - TableRow row1, row2, row3; - TableCell cell; - table.Rows.Add(row1 = new TableRow()); - table.Rows.Add(row2 = new TableRow()); - table.Rows.Add(row3 = new TableRow()); - - // top row, left cell - cell = new TableCell(); try { - this.label = new HtmlGenericControl("label"); - this.label.InnerText = LabelTextDefault; - cell.Controls.Add(this.label); - row1.Cells.Add(cell); - } catch { - cell.Dispose(); - throw; - } + TableRow row1, row2, row3; + TableCell cell; + table.Rows.Add(row1 = new TableRow()); + table.Rows.Add(row2 = new TableRow()); + table.Rows.Add(row3 = new TableRow()); + + // top row, left cell + cell = new TableCell(); + try { + this.label = new HtmlGenericControl("label"); + this.label.InnerText = LabelTextDefault; + cell.Controls.Add(this.label); + row1.Cells.Add(cell); + } catch { + cell.Dispose(); + throw; + } - // top row, middle cell - cell = new TableCell(); - try { - cell.Controls.Add(new InPlaceControl(this)); - row1.Cells.Add(cell); - } catch { - cell.Dispose(); - throw; - } + // top row, middle cell + cell = new TableCell(); + try { + cell.Controls.Add(new InPlaceControl(this)); + row1.Cells.Add(cell); + } catch { + cell.Dispose(); + throw; + } - // top row, right cell - cell = new TableCell(); - try { - this.loginButton = new Button(); - this.loginButton.ID = this.ID + "_loginButton"; - this.loginButton.Text = ButtonTextDefault; - this.loginButton.ToolTip = ButtonToolTipDefault; - this.loginButton.Click += this.LoginButton_Click; - this.loginButton.ValidationGroup = ValidationGroupDefault; + // top row, right cell + cell = new TableCell(); + try { + this.loginButton = new Button(); + this.loginButton.ID = this.ID + "_loginButton"; + this.loginButton.Text = ButtonTextDefault; + this.loginButton.ToolTip = ButtonToolTipDefault; + this.loginButton.Click += this.LoginButton_Click; + this.loginButton.ValidationGroup = ValidationGroupDefault; #if !Mono - this.panel.DefaultButton = this.loginButton.ID; + this.panel.DefaultButton = this.loginButton.ID; #endif - cell.Controls.Add(this.loginButton); - row1.Cells.Add(cell); - } catch { - cell.Dispose(); - throw; - } + cell.Controls.Add(this.loginButton); + row1.Cells.Add(cell); + } catch { + cell.Dispose(); + throw; + } - // middle row, left cell - row2.Cells.Add(new TableCell()); + // middle row, left cell + row2.Cells.Add(new TableCell()); + + // middle row, middle cell + cell = new TableCell(); + try { + cell.Style[HtmlTextWriterStyle.Color] = "gray"; + cell.Style[HtmlTextWriterStyle.FontSize] = "smaller"; + this.requiredValidator = new RequiredFieldValidator(); + this.requiredValidator.ErrorMessage = RequiredTextDefault + RequiredTextSuffix; + this.requiredValidator.Text = RequiredTextDefault + RequiredTextSuffix; + this.requiredValidator.Display = ValidatorDisplay.Dynamic; + this.requiredValidator.ValidationGroup = ValidationGroupDefault; + cell.Controls.Add(this.requiredValidator); + this.identifierFormatValidator = new CustomValidator(); + this.identifierFormatValidator.ErrorMessage = UriFormatTextDefault + RequiredTextSuffix; + this.identifierFormatValidator.Text = UriFormatTextDefault + RequiredTextSuffix; + this.identifierFormatValidator.ServerValidate += this.IdentifierFormatValidator_ServerValidate; + this.identifierFormatValidator.Enabled = UriValidatorEnabledDefault; + this.identifierFormatValidator.Display = ValidatorDisplay.Dynamic; + this.identifierFormatValidator.ValidationGroup = ValidationGroupDefault; + cell.Controls.Add(this.identifierFormatValidator); + this.errorLabel = new Label(); + this.errorLabel.EnableViewState = false; + this.errorLabel.ForeColor = System.Drawing.Color.Red; + this.errorLabel.Style[HtmlTextWriterStyle.Display] = "block"; // puts it on its own line + this.errorLabel.Visible = false; + cell.Controls.Add(this.errorLabel); + this.examplePrefixLabel = new Label(); + this.examplePrefixLabel.Text = ExamplePrefixDefault; + cell.Controls.Add(this.examplePrefixLabel); + cell.Controls.Add(new LiteralControl(" ")); + this.exampleUrlLabel = new Label(); + this.exampleUrlLabel.Font.Bold = true; + this.exampleUrlLabel.Text = ExampleUrlDefault; + cell.Controls.Add(this.exampleUrlLabel); + row2.Cells.Add(cell); + } catch { + cell.Dispose(); + throw; + } - // middle row, middle cell - cell = new TableCell(); - try { - cell.Style[HtmlTextWriterStyle.Color] = "gray"; - cell.Style[HtmlTextWriterStyle.FontSize] = "smaller"; - this.requiredValidator = new RequiredFieldValidator(); - this.requiredValidator.ErrorMessage = RequiredTextDefault + RequiredTextSuffix; - this.requiredValidator.Text = RequiredTextDefault + RequiredTextSuffix; - this.requiredValidator.Display = ValidatorDisplay.Dynamic; - this.requiredValidator.ValidationGroup = ValidationGroupDefault; - cell.Controls.Add(this.requiredValidator); - this.identifierFormatValidator = new CustomValidator(); - this.identifierFormatValidator.ErrorMessage = UriFormatTextDefault + RequiredTextSuffix; - this.identifierFormatValidator.Text = UriFormatTextDefault + RequiredTextSuffix; - this.identifierFormatValidator.ServerValidate += this.IdentifierFormatValidator_ServerValidate; - this.identifierFormatValidator.Enabled = UriValidatorEnabledDefault; - this.identifierFormatValidator.Display = ValidatorDisplay.Dynamic; - this.identifierFormatValidator.ValidationGroup = ValidationGroupDefault; - cell.Controls.Add(this.identifierFormatValidator); - this.errorLabel = new Label(); - this.errorLabel.EnableViewState = false; - this.errorLabel.ForeColor = System.Drawing.Color.Red; - this.errorLabel.Style[HtmlTextWriterStyle.Display] = "block"; // puts it on its own line - this.errorLabel.Visible = false; - cell.Controls.Add(this.errorLabel); - this.examplePrefixLabel = new Label(); - this.examplePrefixLabel.Text = ExamplePrefixDefault; - cell.Controls.Add(this.examplePrefixLabel); - cell.Controls.Add(new LiteralControl(" ")); - this.exampleUrlLabel = new Label(); - this.exampleUrlLabel.Font.Bold = true; - this.exampleUrlLabel.Text = ExampleUrlDefault; - cell.Controls.Add(this.exampleUrlLabel); - row2.Cells.Add(cell); - } catch { - cell.Dispose(); - throw; - } + // middle row, right cell + cell = new TableCell(); + try { + cell.Style[HtmlTextWriterStyle.Color] = "gray"; + cell.Style[HtmlTextWriterStyle.FontSize] = "smaller"; + cell.Style[HtmlTextWriterStyle.TextAlign] = "center"; + this.registerLink = new HyperLink(); + this.registerLink.Text = RegisterTextDefault; + this.registerLink.ToolTip = RegisterToolTipDefault; + this.registerLink.NavigateUrl = RegisterUrlDefault; + this.registerLink.Visible = RegisterVisibleDefault; + cell.Controls.Add(this.registerLink); + row2.Cells.Add(cell); + } catch { + cell.Dispose(); + throw; + } - // middle row, right cell - cell = new TableCell(); - try { - cell.Style[HtmlTextWriterStyle.Color] = "gray"; - cell.Style[HtmlTextWriterStyle.FontSize] = "smaller"; - cell.Style[HtmlTextWriterStyle.TextAlign] = "center"; - this.registerLink = new HyperLink(); - this.registerLink.Text = RegisterTextDefault; - this.registerLink.ToolTip = RegisterToolTipDefault; - this.registerLink.NavigateUrl = RegisterUrlDefault; - this.registerLink.Visible = RegisterVisibleDefault; - cell.Controls.Add(this.registerLink); - row2.Cells.Add(cell); - } catch { - cell.Dispose(); - throw; - } + // bottom row, left cell + cell = new TableCell(); + row3.Cells.Add(cell); - // bottom row, left cell - cell = new TableCell(); - row3.Cells.Add(cell); + // bottom row, middle cell + cell = new TableCell(); + try { + this.rememberMeCheckBox = new CheckBox(); + this.rememberMeCheckBox.Text = RememberMeTextDefault; + this.rememberMeCheckBox.Checked = this.UsePersistentCookie != LogOnPersistence.Session; + this.rememberMeCheckBox.Visible = RememberMeVisibleDefault; + this.rememberMeCheckBox.CheckedChanged += this.RememberMeCheckBox_CheckedChanged; + cell.Controls.Add(this.rememberMeCheckBox); + row3.Cells.Add(cell); + } catch { + cell.Dispose(); + throw; + } - // bottom row, middle cell - cell = new TableCell(); - try { - this.rememberMeCheckBox = new CheckBox(); - this.rememberMeCheckBox.Text = RememberMeTextDefault; - this.rememberMeCheckBox.Checked = this.UsePersistentCookie != LogOnPersistence.Session; - this.rememberMeCheckBox.Visible = RememberMeVisibleDefault; - this.rememberMeCheckBox.CheckedChanged += this.RememberMeCheckBox_CheckedChanged; - cell.Controls.Add(this.rememberMeCheckBox); - row3.Cells.Add(cell); - } catch { - cell.Dispose(); - throw; - } + // bottom row, right cell + cell = new TableCell(); + try { + row3.Cells.Add(cell); + } catch { + cell.Dispose(); + throw; + } - // bottom row, right cell - cell = new TableCell(); - try { - row3.Cells.Add(cell); + // this sets all the controls' tab indexes + this.TabIndex = TabIndexDefault; + + this.panel.Controls.Add(table); } catch { - cell.Dispose(); + table.Dispose(); throw; } - // this sets all the controls' tab indexes - this.TabIndex = TabIndexDefault; - - this.panel.Controls.Add(table); - this.idselectorJavascript = new Literal(); this.panel.Controls.Add(this.idselectorJavascript); } @@ -849,6 +855,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// Renders the child controls. /// </summary> /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the rendered content.</param> + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Web.UI.WebControls.Literal.set_Text(System.String)", Justification = "By design"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "idselector", Justification = "HTML"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "charset", Justification = "html"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "src", Justification = "html"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "openidselector", Justification = "html"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "idselectorinputid", Justification = "html")] protected override void RenderChildren(HtmlTextWriter writer) { if (!this.DesignMode) { this.label.Attributes["for"] = this.ClientID; diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs index a5fdf9b..57c1f05 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs @@ -39,6 +39,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <summary> /// Provides the programmatic facilities to act as an OpenID relying party. /// </summary> + [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Unavoidable")] [ContractVerification(true)] public class OpenIdRelyingParty : IDisposable { /// <summary> @@ -108,6 +109,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> /// <param name="associationStore">The association store. If null, the relying party will always operate in "dumb mode".</param> /// <param name="nonceStore">The nonce store to use. If null, the relying party will always operate in "dumb mode".</param> + [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Unavoidable")] private OpenIdRelyingParty(IAssociationStore<Uri> associationStore, INonceStore nonceStore) { // If we are a smart-mode RP (supporting associations), then we MUST also be // capable of storing nonces to prevent replay attacks. @@ -542,15 +544,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { NegativeAssertionResponse negativeAssertion; IndirectSignedResponse positiveExtensionOnly; if ((positiveAssertion = message as PositiveAssertionResponse) != null) { - if (this.EndpointFilter != null) { - // We need to make sure that this assertion is coming from an endpoint - // that the host deems acceptable. - var providerEndpoint = new SimpleXrdsProviderEndpoint(positiveAssertion); - ErrorUtilities.VerifyProtocol( - this.EndpointFilter(providerEndpoint), - OpenIdStrings.PositiveAssertionFromNonWhitelistedProvider, - providerEndpoint.Uri); - } + // We need to make sure that this assertion is coming from an endpoint + // that the host deems acceptable. + var providerEndpoint = new SimpleXrdsProviderEndpoint(positiveAssertion); + ErrorUtilities.VerifyProtocol( + this.FilterEndpoint(providerEndpoint), + OpenIdStrings.PositiveAssertionFromNonQualifiedProvider, + providerEndpoint.Uri); var response = new PositiveAuthenticationResponse(positiveAssertion, this); foreach (var behavior in this.Behaviors) { @@ -660,8 +660,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </remarks> internal static OpenIdRelyingParty CreateNonVerifying() { OpenIdRelyingParty rp = new OpenIdRelyingParty(); - rp.Channel = OpenIdChannel.CreateNonVerifyingChannel(); - return rp; + try { + rp.Channel = OpenIdChannel.CreateNonVerifyingChannel(); + return rp; + } catch { + rp.Dispose(); + throw; + } } /// <summary> @@ -672,13 +677,14 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <returns> /// The HTTP response to send to this HTTP request. /// </returns> + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "OpenID", Justification = "real word"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "iframe", Justification = "Code contracts")] internal OutgoingWebResponse ProcessResponseFromPopup(HttpRequestInfo request, Action<AuthenticationStatus> callback) { Contract.Requires<ArgumentNullException>(request != null); Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null); string extensionsJson = null; var authResponse = this.NonVerifyingRelyingParty.GetResponse(); - ErrorUtilities.VerifyProtocol(authResponse != null, "OpenID popup window or iframe did not recognize an OpenID response in the request."); + ErrorUtilities.VerifyProtocol(authResponse != null, OpenIdStrings.PopupRedirectMissingResponse); // Give the caller a chance to notify the hosting page and fill up the clientScriptExtensions collection. if (callback != null) { @@ -761,6 +767,38 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> + /// Checks whether a given OP Endpoint is permitted by the host relying party. + /// </summary> + /// <param name="endpoint">The OP endpoint.</param> + /// <returns><c>true</c> if the OP Endpoint is allowed; <c>false</c> otherwise.</returns> + protected internal bool FilterEndpoint(IProviderEndpoint endpoint) { + if (this.SecuritySettings.RejectAssertionsFromUntrustedProviders) { + if (!this.SecuritySettings.TrustedProviderEndpoints.Contains(endpoint.Uri)) { + Logger.OpenId.InfoFormat("Filtering out OP endpoint {0} because it is not on the exclusive trusted provider whitelist.", endpoint.Uri.AbsoluteUri); + return false; + } + } + + if (endpoint.Version < Protocol.Lookup(this.SecuritySettings.MinimumRequiredOpenIdVersion).Version) { + Logger.OpenId.InfoFormat( + "Filtering out OP endpoint {0} because it implements OpenID {1} but this relying party requires OpenID {2} or later.", + endpoint.Uri.AbsoluteUri, + endpoint.Version, + Protocol.Lookup(this.SecuritySettings.MinimumRequiredOpenIdVersion).Version); + return false; + } + + if (this.EndpointFilter != null) { + if (!this.EndpointFilter(endpoint)) { + Logger.OpenId.InfoFormat("Filtering out OP endpoint {0} because the host rejected it.", endpoint.Uri.AbsoluteUri); + return false; + } + } + + return true; + } + + /// <summary> /// Releases unmanaged and - optionally - managed resources /// </summary> /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs index 62f6554..7cda8e0 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs @@ -546,7 +546,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <summary> /// Enables a server control to perform final clean up before it is released from memory. /// </summary> - [SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Base class doesn't implement virtual Dispose(bool), so we must call its Dispose() method.")] + [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "Unavoidable because base class does not expose a protected virtual Dispose(bool) method."), SuppressMessage("Microsoft.Design", "CA1063:ImplementIDisposableCorrectly", Justification = "Base class doesn't implement virtual Dispose(bool), so we must call its Dispose() method.")] public sealed override void Dispose() { this.Dispose(true); base.Dispose(); @@ -809,6 +809,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// Configures the relying party. /// </summary> /// <param name="relyingParty">The relying party.</param> + [SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "relyingParty", Justification = "This makes it possible for overrides to see the value before it is set on a field.")] protected virtual void ConfigureRelyingParty(OpenIdRelyingParty relyingParty) { Contract.Requires<ArgumentNullException>(relyingParty != null); diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs index a7686c5..fc6d4c7 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs @@ -8,6 +8,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { using System; using System.Collections.Generic; using System.Collections.ObjectModel; + using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; using System.Linq; using DotNetOpenAuth.Messaging; @@ -28,6 +30,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { this.PrivateSecretMaximumAge = TimeSpan.FromDays(7); this.ProtectDownlevelReplayAttacks = ProtectDownlevelReplayAttacksDefault; this.AllowApproximateIdentifierDiscovery = true; + this.TrustedProviderEndpoints = new HashSet<Uri>(); } /// <summary> @@ -143,6 +146,19 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { public bool AllowApproximateIdentifierDiscovery { get; set; } /// <summary> + /// Gets the set of trusted OpenID Provider Endpoint URIs. + /// </summary> + public HashSet<Uri> TrustedProviderEndpoints { get; private set; } + + /// <summary> + /// Gets or sets a value indicating whether any login attempt coming from an OpenID Provider Endpoint that is not on this + /// whitelist of trusted OP Endpoints will be rejected. If the trusted providers list is empty and this value + /// is true, all assertions are rejected. + /// </summary> + /// <value>Default is <c>false</c>.</value> + public bool RejectAssertionsFromUntrustedProviders { get; set; } + + /// <summary> /// Gets or sets a value indicating whether special measures are taken to /// protect users from replay attacks when those users' identities are hosted /// by OpenID 1.x Providers. diff --git a/src/DotNetOpenAuth/OpenId/RelyingPartyDescription.cs b/src/DotNetOpenAuth/OpenId/RelyingPartyDescription.cs index 279ca30..7926e8f 100644 --- a/src/DotNetOpenAuth/OpenId/RelyingPartyDescription.cs +++ b/src/DotNetOpenAuth/OpenId/RelyingPartyDescription.cs @@ -7,6 +7,7 @@ namespace DotNetOpenAuth.OpenId { using System; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Linq; using System.Text; @@ -50,6 +51,7 @@ namespace DotNetOpenAuth.OpenId { /// </summary> /// <param name="supportedServiceTypeUris">The supported service type URIs.</param> /// <returns>The best OpenID protocol version to use when communicating with this Provider.</returns> + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "OpenID", Justification = "Spelling correct")] private static Protocol GetProtocolFromServices(string[] supportedServiceTypeUris) { Protocol protocol = Protocol.FindBestVersion(p => p.RPReturnToTypeURI, supportedServiceTypeUris); if (protocol == null) { diff --git a/src/DotNetOpenAuth/OpenId/UriIdentifier.cs b/src/DotNetOpenAuth/OpenId/UriIdentifier.cs index 48ed7f2..145a394 100644 --- a/src/DotNetOpenAuth/OpenId/UriIdentifier.cs +++ b/src/DotNetOpenAuth/OpenId/UriIdentifier.cs @@ -480,7 +480,7 @@ namespace DotNetOpenAuth.OpenId { Contract.Requires<InternalErrorException>(schemeSubstitution); Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>())); - int delimiterIndex = normal.IndexOf(Uri.SchemeDelimiter); + int delimiterIndex = normal.IndexOf(Uri.SchemeDelimiter, StringComparison.Ordinal); string normalScheme = delimiterIndex < 0 ? normal : normal.Substring(0, delimiterIndex); string nonCompressingScheme; if (string.Equals(normalScheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase) || diff --git a/src/DotNetOpenAuth/Xrds/XrdsDocument.cs b/src/DotNetOpenAuth/Xrds/XrdsDocument.cs index e2c2d72..040c994 100644 --- a/src/DotNetOpenAuth/Xrds/XrdsDocument.cs +++ b/src/DotNetOpenAuth/Xrds/XrdsDocument.cs @@ -6,6 +6,7 @@ namespace DotNetOpenAuth.Xrds { using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Xml; @@ -51,6 +52,7 @@ namespace DotNetOpenAuth.Xrds { /// Initializes a new instance of the <see cref="XrdsDocument"/> class. /// </summary> /// <param name="xml">The text that is the XRDS document.</param> + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Fixing would decrease readability, and not likely avoid any finalizer on a StringReader anyway.")] public XrdsDocument(string xml) : this(new XPathDocument(new StringReader(xml)).CreateNavigator()) { } diff --git a/src/DotNetOpenAuth/XrdsPublisher.cs b/src/DotNetOpenAuth/XrdsPublisher.cs index 83d82ff..03c32c1 100644 --- a/src/DotNetOpenAuth/XrdsPublisher.cs +++ b/src/DotNetOpenAuth/XrdsPublisher.cs @@ -208,7 +208,7 @@ namespace DotNetOpenAuth { /// Renders the HTTP Header and/or HTML HEAD tags. /// </summary> /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the server control content.</param> - [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Uri(Uri, string) accepts second arguments that Uri(Uri, new Uri(string)) does not that we must support.")] + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.Contracts.__ContractsRuntime.Assume(System.Boolean,System.String,System.String)", Justification = "Code contracts"), SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Uri(Uri, string) accepts second arguments that Uri(Uri, new Uri(string)) does not that we must support.")] protected override void Render(HtmlTextWriter writer) { Contract.Assume(writer != null, "Missing contract."); if (this.Enabled && this.Visible && !string.IsNullOrEmpty(this.XrdsUrl)) { diff --git a/src/version.txt b/src/version.txt index 2aa5131..7921bd0 100644 --- a/src/version.txt +++ b/src/version.txt @@ -1 +1 @@ -3.4.7 +3.4.8 |