summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2008-12-24 21:47:00 -0800
committerAndrew <andrewarnott@gmail.com>2008-12-24 22:04:44 -0800
commitd072a8351b794f6ef4422db665bbe49a7af0121f (patch)
tree931a8ed69b7190c6ef090fc8189255134b35511e /src
parent5608109ed2f7bd824197b557e0adf3bee1395607 (diff)
downloadDotNetOpenAuth-d072a8351b794f6ef4422db665bbe49a7af0121f.zip
DotNetOpenAuth-d072a8351b794f6ef4422db665bbe49a7af0121f.tar.gz
DotNetOpenAuth-d072a8351b794f6ef4422db665bbe49a7af0121f.tar.bz2
Added new OpenID 1.x compatibility-supporting binding elements.
This adds: * callback arg support * signed return_to to verify callback args and URL are untampered with * Custom nonces to protect RPs against replay attacks when working with 1.0 OPs.
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs582
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs301
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs19
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs8
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/ChannelElements/OpenIdChannelTests.cs204
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/OpenIdCoordinator.cs94
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs70
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj5
-rw-r--r--src/DotNetOpenAuth/Messaging/Bindings/ExpiredMessageException.cs74
-rw-r--r--src/DotNetOpenAuth/Messaging/Bindings/StandardExpirationBindingElement.cs248
-rw-r--r--src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs304
-rw-r--r--src/DotNetOpenAuth/Messaging/Channel.cs96
-rw-r--r--src/DotNetOpenAuth/Messaging/ErrorUtilities.cs253
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs1008
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingStrings.resx532
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingUtilities.cs18
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs116
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/IPrivateSecretStore.cs18
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs57
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs113
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs179
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs71
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/SignedResponseRequest.cs12
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs9
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdStrings.resx3
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs20
-rw-r--r--src/DotNetOpenAuth/OpenId/PrivateSecretMemoryStore.cs15
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs218
28 files changed, 2669 insertions, 1978 deletions
diff --git a/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs b/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs
index 7033382..6bf7130 100644
--- a/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/ChannelTests.cs
@@ -1,290 +1,292 @@
-//-----------------------------------------------------------------------
-// <copyright file="ChannelTests.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.Messaging {
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Net;
- using System.Web;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Bindings;
- using DotNetOpenAuth.Test.Mocks;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
-
- [TestClass]
- public class ChannelTests : MessagingTestBase {
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void CtorNull() {
- // This bad channel is deliberately constructed to pass null to
- // its protected base class' constructor.
- new TestBadChannel(true);
- }
-
- [TestMethod]
- public void ReadFromRequestQueryString() {
- this.ParameterizedReceiveTest("GET");
- }
-
- [TestMethod]
- public void ReadFromRequestForm() {
- this.ParameterizedReceiveTest("POST");
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void SendNull() {
- this.Channel.Send(null);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentException))]
- public void SendIndirectedUndirectedMessage() {
- IProtocolMessage message = new TestDirectedMessage(MessageTransport.Indirect);
- this.Channel.Send(message);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentException))]
- public void SendDirectedNoRecipientMessage() {
- IProtocolMessage message = new TestDirectedMessage(MessageTransport.Indirect);
- this.Channel.Send(message);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentException))]
- public void SendInvalidMessageTransport() {
- IProtocolMessage message = new TestDirectedMessage((MessageTransport)100);
- this.Channel.Send(message);
- }
-
- [TestMethod]
- public void SendIndirectMessage301Get() {
- TestDirectedMessage message = new TestDirectedMessage(MessageTransport.Indirect);
- GetStandardTestMessage(FieldFill.CompleteBeforeBindings, message);
- message.Recipient = new Uri("http://provider/path");
- var expected = GetStandardTestFields(FieldFill.CompleteBeforeBindings);
-
- UserAgentResponse response = this.Channel.Send(message);
- Assert.AreEqual(HttpStatusCode.Redirect, response.Status);
- StringAssert.StartsWith(response.Headers[HttpResponseHeader.Location], "http://provider/path");
- foreach (var pair in expected) {
- string key = HttpUtility.UrlEncode(pair.Key);
- string value = HttpUtility.UrlEncode(pair.Value);
- string substring = string.Format("{0}={1}", key, value);
- StringAssert.Contains(response.Headers[HttpResponseHeader.Location], substring);
- }
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void SendIndirectMessage301GetNullMessage() {
- TestBadChannel badChannel = new TestBadChannel(false);
- badChannel.Create301RedirectResponse(null, new Dictionary<string, string>());
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentException))]
- public void SendIndirectMessage301GetEmptyRecipient() {
- TestBadChannel badChannel = new TestBadChannel(false);
- var message = new TestDirectedMessage(MessageTransport.Indirect);
- badChannel.Create301RedirectResponse(message, new Dictionary<string, string>());
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void SendIndirectMessage301GetNullFields() {
- TestBadChannel badChannel = new TestBadChannel(false);
- var message = new TestDirectedMessage(MessageTransport.Indirect);
- message.Recipient = new Uri("http://someserver");
- badChannel.Create301RedirectResponse(message, null);
- }
-
- [TestMethod]
- public void SendIndirectMessageFormPost() {
- // We craft a very large message to force fallback to form POST.
- // We'll also stick some HTML reserved characters in the string value
- // to test proper character escaping.
- var message = new TestDirectedMessage(MessageTransport.Indirect) {
- Age = 15,
- Name = "c<b" + new string('a', 10 * 1024),
- Location = new Uri("http://host/path"),
- Recipient = new Uri("http://provider/path"),
- };
- UserAgentResponse response = this.Channel.Send(message);
- Assert.AreEqual(HttpStatusCode.OK, response.Status, "A form redirect should be an HTTP successful response.");
- Assert.IsNull(response.Headers[HttpResponseHeader.Location], "There should not be a redirection header in the response.");
- string body = response.Body;
- StringAssert.Contains(body, "<form ");
- StringAssert.Contains(body, "action=\"http://provider/path\"");
- StringAssert.Contains(body, "method=\"post\"");
- StringAssert.Contains(body, "<input type=\"hidden\" name=\"age\" value=\"15\" />");
- StringAssert.Contains(body, "<input type=\"hidden\" name=\"Location\" value=\"http://host/path\" />");
- StringAssert.Contains(body, "<input type=\"hidden\" name=\"Name\" value=\"" + HttpUtility.HtmlEncode(message.Name) + "\" />");
- StringAssert.Contains(body, ".submit()", "There should be some javascript to automate form submission.");
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void SendIndirectMessageFormPostNullMessage() {
- TestBadChannel badChannel = new TestBadChannel(false);
- badChannel.CreateFormPostResponse(null, new Dictionary<string, string>());
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentException))]
- public void SendIndirectMessageFormPostEmptyRecipient() {
- TestBadChannel badChannel = new TestBadChannel(false);
- var message = new TestDirectedMessage(MessageTransport.Indirect);
- badChannel.CreateFormPostResponse(message, new Dictionary<string, string>());
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void SendIndirectMessageFormPostNullFields() {
- TestBadChannel badChannel = new TestBadChannel(false);
- var message = new TestDirectedMessage(MessageTransport.Indirect);
- message.Recipient = new Uri("http://someserver");
- badChannel.CreateFormPostResponse(message, null);
- }
-
- /// <summary>
- /// Tests that a direct message is sent when the appropriate message type is provided.
- /// </summary>
- /// <remarks>
- /// Since this is a mock channel that doesn't actually formulate a direct message response,
- /// we just check that the right method was called.
- /// </remarks>
- [TestMethod, ExpectedException(typeof(NotImplementedException), "SendDirectMessageResponse")]
- public void SendDirectMessageResponse() {
- IProtocolMessage message = new TestDirectedMessage {
- Age = 15,
- Name = "Andrew",
- Location = new Uri("http://host/path"),
- };
- this.Channel.Send(message);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void SendIndirectMessageNull() {
- TestBadChannel badChannel = new TestBadChannel(false);
- badChannel.SendIndirectMessage(null);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void ReceiveNull() {
- TestBadChannel badChannel = new TestBadChannel(false);
- badChannel.Receive(null, null);
- }
-
- [TestMethod]
- public void ReceiveUnrecognizedMessage() {
- TestBadChannel badChannel = new TestBadChannel(false);
- Assert.IsNull(badChannel.Receive(new Dictionary<string, string>(), null));
- }
-
- [TestMethod]
- public void ReadFromRequestWithContext() {
- var fields = GetStandardTestFields(FieldFill.AllRequired);
- TestMessage expectedMessage = GetStandardTestMessage(FieldFill.AllRequired);
- HttpRequest request = new HttpRequest("somefile", "http://someurl", MessagingUtilities.CreateQueryString(fields));
- HttpContext.Current = new HttpContext(request, new HttpResponse(new StringWriter()));
- IProtocolMessage message = this.Channel.ReadFromRequest();
- Assert.IsNotNull(message);
- Assert.IsInstanceOfType(message, typeof(TestMessage));
- Assert.AreEqual(expectedMessage.Age, ((TestMessage)message).Age);
- }
-
- [TestMethod, ExpectedException(typeof(InvalidOperationException))]
- public void ReadFromRequestNoContext() {
- TestBadChannel badChannel = new TestBadChannel(false);
- badChannel.ReadFromRequest();
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void ReadFromRequestNull() {
- TestBadChannel badChannel = new TestBadChannel(false);
- badChannel.ReadFromRequest(null);
- }
-
- [TestMethod]
- public void SendReplayProtectedMessageSetsNonce() {
- TestReplayProtectedMessage message = new TestReplayProtectedMessage(MessageTransport.Indirect);
- message.Recipient = new Uri("http://localtest");
-
- this.Channel = CreateChannel(MessageProtections.ReplayProtection);
- this.Channel.Send(message);
- Assert.IsNotNull(((IReplayProtectedProtocolMessage)message).Nonce);
- }
-
- [TestMethod, ExpectedException(typeof(InvalidSignatureException))]
- public void ReceivedInvalidSignature() {
- this.Channel = CreateChannel(MessageProtections.TamperProtection);
- this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, true);
- }
-
- [TestMethod]
- public void ReceivedReplayProtectedMessageJustOnce() {
- this.Channel = CreateChannel(MessageProtections.ReplayProtection);
- this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, false);
- }
-
- [TestMethod, ExpectedException(typeof(ReplayedMessageException))]
- public void ReceivedReplayProtectedMessageTwice() {
- this.Channel = CreateChannel(MessageProtections.ReplayProtection);
- this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, false);
- this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, false);
- }
-
- [TestMethod, ExpectedException(typeof(ProtocolException))]
- public void MessageExpirationWithoutTamperResistance() {
- new TestChannel(
- new TestMessageFactory(),
- new StandardExpirationBindingElement());
- }
-
- [TestMethod, ExpectedException(typeof(ProtocolException))]
- public void TooManyBindingElementsProvidingSameProtection() {
- new TestChannel(
- new TestMessageFactory(),
- new MockSigningBindingElement(),
- new MockSigningBindingElement());
- }
-
- [TestMethod]
- public void BindingElementsOrdering() {
- IChannelBindingElement transformA = new MockTransformationBindingElement("a");
- IChannelBindingElement transformB = new MockTransformationBindingElement("b");
- IChannelBindingElement sign = new MockSigningBindingElement();
- IChannelBindingElement replay = new MockReplayProtectionBindingElement();
- IChannelBindingElement expire = new StandardExpirationBindingElement();
-
- Channel channel = new TestChannel(
- new TestMessageFactory(),
- sign,
- replay,
- expire,
- transformB,
- transformA);
-
- Assert.AreEqual(5, channel.BindingElements.Count);
- Assert.AreSame(transformB, channel.BindingElements[0]);
- Assert.AreSame(transformA, channel.BindingElements[1]);
- Assert.AreSame(replay, channel.BindingElements[2]);
- Assert.AreSame(expire, channel.BindingElements[3]);
- Assert.AreSame(sign, channel.BindingElements[4]);
- }
-
- [TestMethod, ExpectedException(typeof(UnprotectedMessageException))]
- public void InsufficientlyProtectedMessageSent() {
- var message = new TestSignedDirectedMessage(MessageTransport.Direct);
- message.Recipient = new Uri("http://localtest");
- this.Channel.Send(message);
- }
-
- [TestMethod, ExpectedException(typeof(UnprotectedMessageException))]
- public void InsufficientlyProtectedMessageReceived() {
- this.Channel = CreateChannel(MessageProtections.None, MessageProtections.TamperProtection);
- this.ParameterizedReceiveProtectedTest(DateTime.Now, false);
- }
-
- [TestMethod, ExpectedException(typeof(ProtocolException))]
- public void IncomingMessageMissingRequiredParameters() {
- var fields = GetStandardTestFields(FieldFill.IdentifiableButNotAllRequired);
- this.Channel.ReadFromRequest(CreateHttpRequestInfo("GET", fields));
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ChannelTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Net;
+ using System.Web;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.Test.Mocks;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class ChannelTests : MessagingTestBase {
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void CtorNull() {
+ // This bad channel is deliberately constructed to pass null to
+ // its protected base class' constructor.
+ new TestBadChannel(true);
+ }
+
+ [TestMethod]
+ public void ReadFromRequestQueryString() {
+ this.ParameterizedReceiveTest("GET");
+ }
+
+ [TestMethod]
+ public void ReadFromRequestForm() {
+ this.ParameterizedReceiveTest("POST");
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void SendNull() {
+ this.Channel.Send(null);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void SendIndirectedUndirectedMessage() {
+ IProtocolMessage message = new TestDirectedMessage(MessageTransport.Indirect);
+ this.Channel.Send(message);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void SendDirectedNoRecipientMessage() {
+ IProtocolMessage message = new TestDirectedMessage(MessageTransport.Indirect);
+ this.Channel.Send(message);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void SendInvalidMessageTransport() {
+ IProtocolMessage message = new TestDirectedMessage((MessageTransport)100);
+ this.Channel.Send(message);
+ }
+
+ [TestMethod]
+ public void SendIndirectMessage301Get() {
+ TestDirectedMessage message = new TestDirectedMessage(MessageTransport.Indirect);
+ GetStandardTestMessage(FieldFill.CompleteBeforeBindings, message);
+ message.Recipient = new Uri("http://provider/path");
+ var expected = GetStandardTestFields(FieldFill.CompleteBeforeBindings);
+
+ UserAgentResponse response = this.Channel.Send(message);
+ Assert.AreEqual(HttpStatusCode.Redirect, response.Status);
+ StringAssert.StartsWith(response.Headers[HttpResponseHeader.Location], "http://provider/path");
+ foreach (var pair in expected) {
+ string key = HttpUtility.UrlEncode(pair.Key);
+ string value = HttpUtility.UrlEncode(pair.Value);
+ string substring = string.Format("{0}={1}", key, value);
+ StringAssert.Contains(response.Headers[HttpResponseHeader.Location], substring);
+ }
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void SendIndirectMessage301GetNullMessage() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ badChannel.Create301RedirectResponse(null, new Dictionary<string, string>());
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void SendIndirectMessage301GetEmptyRecipient() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ var message = new TestDirectedMessage(MessageTransport.Indirect);
+ badChannel.Create301RedirectResponse(message, new Dictionary<string, string>());
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void SendIndirectMessage301GetNullFields() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ var message = new TestDirectedMessage(MessageTransport.Indirect);
+ message.Recipient = new Uri("http://someserver");
+ badChannel.Create301RedirectResponse(message, null);
+ }
+
+ [TestMethod]
+ public void SendIndirectMessageFormPost() {
+ // We craft a very large message to force fallback to form POST.
+ // We'll also stick some HTML reserved characters in the string value
+ // to test proper character escaping.
+ var message = new TestDirectedMessage(MessageTransport.Indirect) {
+ Age = 15,
+ Name = "c<b" + new string('a', 10 * 1024),
+ Location = new Uri("http://host/path"),
+ Recipient = new Uri("http://provider/path"),
+ };
+ UserAgentResponse response = this.Channel.Send(message);
+ Assert.AreEqual(HttpStatusCode.OK, response.Status, "A form redirect should be an HTTP successful response.");
+ Assert.IsNull(response.Headers[HttpResponseHeader.Location], "There should not be a redirection header in the response.");
+ string body = response.Body;
+ StringAssert.Contains(body, "<form ");
+ StringAssert.Contains(body, "action=\"http://provider/path\"");
+ StringAssert.Contains(body, "method=\"post\"");
+ StringAssert.Contains(body, "<input type=\"hidden\" name=\"age\" value=\"15\" />");
+ StringAssert.Contains(body, "<input type=\"hidden\" name=\"Location\" value=\"http://host/path\" />");
+ StringAssert.Contains(body, "<input type=\"hidden\" name=\"Name\" value=\"" + HttpUtility.HtmlEncode(message.Name) + "\" />");
+ StringAssert.Contains(body, ".submit()", "There should be some javascript to automate form submission.");
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void SendIndirectMessageFormPostNullMessage() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ badChannel.CreateFormPostResponse(null, new Dictionary<string, string>());
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void SendIndirectMessageFormPostEmptyRecipient() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ var message = new TestDirectedMessage(MessageTransport.Indirect);
+ badChannel.CreateFormPostResponse(message, new Dictionary<string, string>());
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void SendIndirectMessageFormPostNullFields() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ var message = new TestDirectedMessage(MessageTransport.Indirect);
+ message.Recipient = new Uri("http://someserver");
+ badChannel.CreateFormPostResponse(message, null);
+ }
+
+ /// <summary>
+ /// Tests that a direct message is sent when the appropriate message type is provided.
+ /// </summary>
+ /// <remarks>
+ /// Since this is a mock channel that doesn't actually formulate a direct message response,
+ /// we just check that the right method was called.
+ /// </remarks>
+ [TestMethod, ExpectedException(typeof(NotImplementedException), "SendDirectMessageResponse")]
+ public void SendDirectMessageResponse() {
+ IProtocolMessage message = new TestDirectedMessage {
+ Age = 15,
+ Name = "Andrew",
+ Location = new Uri("http://host/path"),
+ };
+ this.Channel.Send(message);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void SendIndirectMessageNull() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ badChannel.SendIndirectMessage(null);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void ReceiveNull() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ badChannel.Receive(null, null);
+ }
+
+ [TestMethod]
+ public void ReceiveUnrecognizedMessage() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ Assert.IsNull(badChannel.Receive(new Dictionary<string, string>(), null));
+ }
+
+ [TestMethod]
+ public void ReadFromRequestWithContext() {
+ var fields = GetStandardTestFields(FieldFill.AllRequired);
+ TestMessage expectedMessage = GetStandardTestMessage(FieldFill.AllRequired);
+ HttpRequest request = new HttpRequest("somefile", "http://someurl", MessagingUtilities.CreateQueryString(fields));
+ HttpContext.Current = new HttpContext(request, new HttpResponse(new StringWriter()));
+ IProtocolMessage message = this.Channel.ReadFromRequest();
+ Assert.IsNotNull(message);
+ Assert.IsInstanceOfType(message, typeof(TestMessage));
+ Assert.AreEqual(expectedMessage.Age, ((TestMessage)message).Age);
+ }
+
+ [TestMethod, ExpectedException(typeof(InvalidOperationException))]
+ public void ReadFromRequestNoContext() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ badChannel.ReadFromRequest();
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void ReadFromRequestNull() {
+ TestBadChannel badChannel = new TestBadChannel(false);
+ badChannel.ReadFromRequest(null);
+ }
+
+ [TestMethod]
+ public void SendReplayProtectedMessageSetsNonce() {
+ TestReplayProtectedMessage message = new TestReplayProtectedMessage(MessageTransport.Indirect);
+ message.Recipient = new Uri("http://localtest");
+
+ this.Channel = CreateChannel(MessageProtections.ReplayProtection);
+ this.Channel.Send(message);
+ Assert.IsNotNull(((IReplayProtectedProtocolMessage)message).Nonce);
+ }
+
+ [TestMethod, ExpectedException(typeof(InvalidSignatureException))]
+ public void ReceivedInvalidSignature() {
+ this.Channel = CreateChannel(MessageProtections.TamperProtection);
+ this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, true);
+ }
+
+ [TestMethod]
+ public void ReceivedReplayProtectedMessageJustOnce() {
+ this.Channel = CreateChannel(MessageProtections.ReplayProtection);
+ this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, false);
+ }
+
+ [TestMethod, ExpectedException(typeof(ReplayedMessageException))]
+ public void ReceivedReplayProtectedMessageTwice() {
+ this.Channel = CreateChannel(MessageProtections.ReplayProtection);
+ this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, false);
+ this.ParameterizedReceiveProtectedTest(DateTime.UtcNow, false);
+ }
+
+ [TestMethod, ExpectedException(typeof(ProtocolException))]
+ public void MessageExpirationWithoutTamperResistance() {
+ new TestChannel(
+ new TestMessageFactory(),
+ new StandardExpirationBindingElement());
+ }
+
+ [TestMethod, ExpectedException(typeof(ProtocolException))]
+ public void TooManyBindingElementsProvidingSameProtection() {
+ Channel channel = new TestChannel(
+ new TestMessageFactory(),
+ new MockSigningBindingElement(),
+ new MockSigningBindingElement());
+ Channel_Accessor accessor = Channel_Accessor.AttachShadow(channel);
+ accessor.PrepareMessageForSending(new TestSignedDirectedMessage());
+ }
+
+ [TestMethod]
+ public void BindingElementsOrdering() {
+ IChannelBindingElement transformA = new MockTransformationBindingElement("a");
+ IChannelBindingElement transformB = new MockTransformationBindingElement("b");
+ IChannelBindingElement sign = new MockSigningBindingElement();
+ IChannelBindingElement replay = new MockReplayProtectionBindingElement();
+ IChannelBindingElement expire = new StandardExpirationBindingElement();
+
+ Channel channel = new TestChannel(
+ new TestMessageFactory(),
+ sign,
+ replay,
+ expire,
+ transformB,
+ transformA);
+
+ Assert.AreEqual(5, channel.BindingElements.Count);
+ Assert.AreSame(transformB, channel.BindingElements[0]);
+ Assert.AreSame(transformA, channel.BindingElements[1]);
+ Assert.AreSame(replay, channel.BindingElements[2]);
+ Assert.AreSame(expire, channel.BindingElements[3]);
+ Assert.AreSame(sign, channel.BindingElements[4]);
+ }
+
+ [TestMethod, ExpectedException(typeof(UnprotectedMessageException))]
+ public void InsufficientlyProtectedMessageSent() {
+ var message = new TestSignedDirectedMessage(MessageTransport.Direct);
+ message.Recipient = new Uri("http://localtest");
+ this.Channel.Send(message);
+ }
+
+ [TestMethod, ExpectedException(typeof(UnprotectedMessageException))]
+ public void InsufficientlyProtectedMessageReceived() {
+ this.Channel = CreateChannel(MessageProtections.None, MessageProtections.TamperProtection);
+ this.ParameterizedReceiveProtectedTest(DateTime.Now, false);
+ }
+
+ [TestMethod, ExpectedException(typeof(ProtocolException))]
+ public void IncomingMessageMissingRequiredParameters() {
+ var fields = GetStandardTestFields(FieldFill.IdentifiableButNotAllRequired);
+ this.Channel.ReadFromRequest(CreateHttpRequestInfo("GET", fields));
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs
index 7a96718..1731afb 100644
--- a/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs
@@ -1,149 +1,152 @@
-//-----------------------------------------------------------------------
-// <copyright file="CoordinatingChannel.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.Mocks {
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading;
- using DotNetOpenAuth.Messaging;
-
- internal class CoordinatingChannel : Channel {
- private Channel wrappedChannel;
- private EventWaitHandle incomingMessageSignal = new AutoResetEvent(false);
- private IProtocolMessage incomingMessage;
- private Action<IProtocolMessage> incomingMessageFilter;
- private Action<IProtocolMessage> outgoingMessageFilter;
-
- internal CoordinatingChannel(Channel wrappedChannel, Action<IProtocolMessage> incomingMessageFilter, Action<IProtocolMessage> outgoingMessageFilter)
- : base(GetMessageFactory(wrappedChannel), wrappedChannel.BindingElements.ToArray()) {
- ErrorUtilities.VerifyArgumentNotNull(wrappedChannel, "wrappedChannel");
-
- this.wrappedChannel = wrappedChannel;
- this.incomingMessageFilter = incomingMessageFilter;
- this.outgoingMessageFilter = outgoingMessageFilter;
- }
-
- /// <summary>
- /// Gets or sets the coordinating channel used by the other party.
- /// </summary>
- internal CoordinatingChannel RemoteChannel { get; set; }
-
- protected internal override HttpRequestInfo GetRequestFromContext() {
- return new HttpRequestInfo((IDirectedProtocolMessage)this.AwaitIncomingMessage());
- }
-
- protected override IProtocolMessage RequestInternal(IDirectedProtocolMessage request) {
- this.ProcessMessageFilter(request, true);
- HttpRequestInfo requestInfo = this.SpoofHttpMethod(request);
- // Drop the outgoing message in the other channel's in-slot and let them know it's there.
- this.RemoteChannel.incomingMessage = requestInfo.Message;
- this.RemoteChannel.incomingMessageSignal.Set();
- // Now wait for a response...
- IProtocolMessage response = this.AwaitIncomingMessage();
- this.ProcessMessageFilter(response, false);
- return response;
- }
-
- protected override UserAgentResponse SendDirectMessageResponse(IProtocolMessage response) {
- this.ProcessMessageFilter(response, true);
- this.RemoteChannel.incomingMessage = CloneSerializedParts(response, null);
- this.RemoteChannel.incomingMessageSignal.Set();
- return null;
- }
-
- protected override UserAgentResponse SendIndirectMessage(IDirectedProtocolMessage message) {
- this.ProcessMessageFilter(message, true);
- // In this mock transport, direct and indirect messages are the same.
- return this.SendDirectMessageResponse(message);
- }
-
- protected override IDirectedProtocolMessage ReadFromRequestInternal(HttpRequestInfo request) {
- this.ProcessMessageFilter(request.Message, false);
- return request.Message;
- }
-
- protected override IDictionary<string, string> ReadFromResponseInternal(DirectWebResponse response) {
- Channel_Accessor accessor = Channel_Accessor.AttachShadow(this.wrappedChannel);
- return accessor.ReadFromResponseInternal(response);
- }
-
- protected override void VerifyMessageAfterReceiving(IProtocolMessage message) {
- Channel_Accessor accessor = Channel_Accessor.AttachShadow(this.wrappedChannel);
- accessor.VerifyMessageAfterReceiving(message);
- }
-
- /// <summary>
- /// Spoof HTTP request information for signing/verification purposes.
- /// </summary>
- /// <param name="message">The message to add a pretend HTTP method to.</param>
- /// <returns>A spoofed HttpRequestInfo that wraps the new message.</returns>
- protected virtual HttpRequestInfo SpoofHttpMethod(IDirectedProtocolMessage message) {
- HttpRequestInfo requestInfo = new HttpRequestInfo(message);
-
- requestInfo.Message = this.CloneSerializedParts(message, requestInfo);
-
- return requestInfo;
- }
-
- protected virtual T CloneSerializedParts<T>(T message, HttpRequestInfo requestInfo) where T : class, IProtocolMessage {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
-
- IProtocolMessage clonedMessage;
- MessageSerializer serializer = MessageSerializer.Get(message.GetType());
- var fields = serializer.Serialize(message);
-
- MessageReceivingEndpoint recipient = null;
- var directedMessage = message as IDirectedProtocolMessage;
- var directResponse = message as IDirectResponseProtocolMessage;
- if (directedMessage != null && directedMessage.IsRequest()) {
- if (directedMessage.Recipient != null) {
- recipient = new MessageReceivingEndpoint(directedMessage.Recipient, directedMessage.HttpMethods);
- }
-
- clonedMessage = this.RemoteChannel.MessageFactory.GetNewRequestMessage(recipient, fields);
- } else if (directResponse != null && directResponse.IsDirectResponse()) {
- clonedMessage = this.RemoteChannel.MessageFactory.GetNewResponseMessage(directResponse.OriginatingRequest, fields);
- } else {
- throw new InvalidOperationException("Totally expected a message to implement one of the two derived interface types.");
- }
-
- ErrorUtilities.VerifyInternal(clonedMessage != null, "Message factory did not generate a message instance for " + message.GetType().Name);
-
- // Fill the cloned message with data.
- serializer.Deserialize(fields, clonedMessage);
-
- return (T)clonedMessage;
- }
-
- private static IMessageFactory GetMessageFactory(Channel channel) {
- ErrorUtilities.VerifyArgumentNotNull(channel, "channel");
-
- Channel_Accessor accessor = Channel_Accessor.AttachShadow(channel);
- return accessor.MessageFactory;
- }
-
- private IProtocolMessage AwaitIncomingMessage() {
- this.incomingMessageSignal.WaitOne();
- IProtocolMessage response = this.incomingMessage;
- this.incomingMessage = null;
- return response;
- }
-
- private void ProcessMessageFilter(IProtocolMessage message, bool outgoing) {
- if (outgoing) {
- if (this.outgoingMessageFilter != null) {
- this.outgoingMessageFilter(message);
- }
- } else {
- if (this.incomingMessageFilter != null) {
- this.incomingMessageFilter(message);
- }
- }
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="CoordinatingChannel.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.Mocks {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using System.Threading;
+ using DotNetOpenAuth.Messaging;
+
+ internal class CoordinatingChannel : Channel {
+ private Channel wrappedChannel;
+ private EventWaitHandle incomingMessageSignal = new AutoResetEvent(false);
+ private IProtocolMessage incomingMessage;
+ private Action<IProtocolMessage> incomingMessageFilter;
+ private Action<IProtocolMessage> outgoingMessageFilter;
+
+ internal CoordinatingChannel(Channel wrappedChannel, Action<IProtocolMessage> incomingMessageFilter, Action<IProtocolMessage> outgoingMessageFilter)
+ : base(GetMessageFactory(wrappedChannel), wrappedChannel.BindingElements.ToArray()) {
+ ErrorUtilities.VerifyArgumentNotNull(wrappedChannel, "wrappedChannel");
+
+ this.wrappedChannel = wrappedChannel;
+ this.incomingMessageFilter = incomingMessageFilter;
+ this.outgoingMessageFilter = outgoingMessageFilter;
+
+ // Preserve any customized binding element ordering.
+ this.CustomizeBindingElementOrder(this.wrappedChannel.OutgoingBindingElements, this.wrappedChannel.IncomingBindingElements);
+ }
+
+ /// <summary>
+ /// Gets or sets the coordinating channel used by the other party.
+ /// </summary>
+ internal CoordinatingChannel RemoteChannel { get; set; }
+
+ protected internal override HttpRequestInfo GetRequestFromContext() {
+ return new HttpRequestInfo((IDirectedProtocolMessage)this.AwaitIncomingMessage());
+ }
+
+ protected override IProtocolMessage RequestInternal(IDirectedProtocolMessage request) {
+ this.ProcessMessageFilter(request, true);
+ HttpRequestInfo requestInfo = this.SpoofHttpMethod(request);
+ // Drop the outgoing message in the other channel's in-slot and let them know it's there.
+ this.RemoteChannel.incomingMessage = requestInfo.Message;
+ this.RemoteChannel.incomingMessageSignal.Set();
+ // Now wait for a response...
+ IProtocolMessage response = this.AwaitIncomingMessage();
+ this.ProcessMessageFilter(response, false);
+ return response;
+ }
+
+ protected override UserAgentResponse SendDirectMessageResponse(IProtocolMessage response) {
+ this.ProcessMessageFilter(response, true);
+ this.RemoteChannel.incomingMessage = CloneSerializedParts(response, null);
+ this.RemoteChannel.incomingMessageSignal.Set();
+ return null;
+ }
+
+ protected override UserAgentResponse SendIndirectMessage(IDirectedProtocolMessage message) {
+ this.ProcessMessageFilter(message, true);
+ // In this mock transport, direct and indirect messages are the same.
+ return this.SendDirectMessageResponse(message);
+ }
+
+ protected override IDirectedProtocolMessage ReadFromRequestInternal(HttpRequestInfo request) {
+ this.ProcessMessageFilter(request.Message, false);
+ return request.Message;
+ }
+
+ protected override IDictionary<string, string> ReadFromResponseInternal(DirectWebResponse response) {
+ Channel_Accessor accessor = Channel_Accessor.AttachShadow(this.wrappedChannel);
+ return accessor.ReadFromResponseInternal(response);
+ }
+
+ protected override void VerifyMessageAfterReceiving(IProtocolMessage message) {
+ Channel_Accessor accessor = Channel_Accessor.AttachShadow(this.wrappedChannel);
+ accessor.VerifyMessageAfterReceiving(message);
+ }
+
+ /// <summary>
+ /// Spoof HTTP request information for signing/verification purposes.
+ /// </summary>
+ /// <param name="message">The message to add a pretend HTTP method to.</param>
+ /// <returns>A spoofed HttpRequestInfo that wraps the new message.</returns>
+ protected virtual HttpRequestInfo SpoofHttpMethod(IDirectedProtocolMessage message) {
+ HttpRequestInfo requestInfo = new HttpRequestInfo(message);
+
+ requestInfo.Message = this.CloneSerializedParts(message, requestInfo);
+
+ return requestInfo;
+ }
+
+ protected virtual T CloneSerializedParts<T>(T message, HttpRequestInfo requestInfo) where T : class, IProtocolMessage {
+ ErrorUtilities.VerifyArgumentNotNull(message, "message");
+
+ IProtocolMessage clonedMessage;
+ MessageSerializer serializer = MessageSerializer.Get(message.GetType());
+ var fields = serializer.Serialize(message);
+
+ MessageReceivingEndpoint recipient = null;
+ var directedMessage = message as IDirectedProtocolMessage;
+ var directResponse = message as IDirectResponseProtocolMessage;
+ if (directedMessage != null && directedMessage.IsRequest()) {
+ if (directedMessage.Recipient != null) {
+ recipient = new MessageReceivingEndpoint(directedMessage.Recipient, directedMessage.HttpMethods);
+ }
+
+ clonedMessage = this.RemoteChannel.MessageFactory.GetNewRequestMessage(recipient, fields);
+ } else if (directResponse != null && directResponse.IsDirectResponse()) {
+ clonedMessage = this.RemoteChannel.MessageFactory.GetNewResponseMessage(directResponse.OriginatingRequest, fields);
+ } else {
+ throw new InvalidOperationException("Totally expected a message to implement one of the two derived interface types.");
+ }
+
+ ErrorUtilities.VerifyInternal(clonedMessage != null, "Message factory did not generate a message instance for " + message.GetType().Name);
+
+ // Fill the cloned message with data.
+ serializer.Deserialize(fields, clonedMessage);
+
+ return (T)clonedMessage;
+ }
+
+ private static IMessageFactory GetMessageFactory(Channel channel) {
+ ErrorUtilities.VerifyArgumentNotNull(channel, "channel");
+
+ Channel_Accessor accessor = Channel_Accessor.AttachShadow(channel);
+ return accessor.MessageFactory;
+ }
+
+ private IProtocolMessage AwaitIncomingMessage() {
+ this.incomingMessageSignal.WaitOne();
+ IProtocolMessage response = this.incomingMessage;
+ this.incomingMessage = null;
+ return response;
+ }
+
+ private void ProcessMessageFilter(IProtocolMessage message, bool outgoing) {
+ if (outgoing) {
+ if (this.outgoingMessageFilter != null) {
+ this.outgoingMessageFilter(message);
+ }
+ } else {
+ if (this.incomingMessageFilter != null) {
+ this.incomingMessageFilter(message);
+ }
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs b/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs
index b02cfee..ca1e5f1 100644
--- a/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs
@@ -16,9 +16,6 @@ namespace DotNetOpenAuth.Test.OpenId {
using DotNetOpenAuth.OpenId.Messages;
using Microsoft.VisualStudio.TestTools.UnitTesting;
- // TODO: make all the tests in this class test every version of the protocol.
- // Currently this fails because we don't have a "token"-like facility of
- // DotNetOpenID yet.
[TestClass]
public class AuthenticationTests : OpenIdTestBase {
[TestInitialize]
@@ -28,7 +25,7 @@ namespace DotNetOpenAuth.Test.OpenId {
[TestMethod]
public void SharedAssociationPositive() {
- this.ParameterizedPositiveAuthenticationTest(Protocol.Default, true, true, false);
+ this.ParameterizedPositiveAuthenticationTest(true, true, false);
}
/// <summary>
@@ -36,17 +33,17 @@ namespace DotNetOpenAuth.Test.OpenId {
/// </summary>
[TestMethod]
public void SharedAssociationTampered() {
- this.ParameterizedPositiveAuthenticationTest(Protocol.Default, true, true, true);
+ this.ParameterizedPositiveAuthenticationTest(true, true, true);
}
[TestMethod]
public void SharedAssociationNegative() {
- this.ParameterizedPositiveAuthenticationTest(Protocol.V11, true, false, false);
+ this.ParameterizedPositiveAuthenticationTest(true, false, false);
}
[TestMethod]
public void PrivateAssociationPositive() {
- this.ParameterizedPositiveAuthenticationTest(Protocol.Default, false, true, false);
+ this.ParameterizedPositiveAuthenticationTest(false, true, false);
}
/// <summary>
@@ -54,12 +51,12 @@ namespace DotNetOpenAuth.Test.OpenId {
/// </summary>
[TestMethod]
public void PrivateAssociationTampered() {
- this.ParameterizedPositiveAuthenticationTest(Protocol.Default, false, true, true);
+ this.ParameterizedPositiveAuthenticationTest(false, true, true);
}
[TestMethod]
public void NoAssociationNegative() {
- this.ParameterizedPositiveAuthenticationTest(Protocol.Default, false, false, false);
+ this.ParameterizedPositiveAuthenticationTest(false, false, false);
}
private void ParameterizedPositiveAuthenticationTest(bool sharedAssociation, bool positive, bool tamper) {
@@ -132,10 +129,10 @@ namespace DotNetOpenAuth.Test.OpenId {
coordinator.IncomingMessageFilter = message => {
var assertion = message as PositiveAssertionResponse;
if (assertion != null) {
- // Alter the Claimed Identifier between the Provider and the Relying Party.
+ // Alter the Local Identifier between the Provider and the Relying Party.
// If the signature binding element does its job, this should cause the RP
// to throw.
- assertion.ClaimedIdentifier = "http://victim";
+ assertion.LocalIdentifier = "http://victim";
}
};
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs
index 6eafb13..c7e445c 100644
--- a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs
@@ -85,6 +85,14 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements {
Assert.AreEqual("extra", ext.Data);
}
+ /// <summary>
+ /// Verifies that unsigned extension responses (where any or all fields are unsigned) are ignored.
+ /// </summary>
+ [TestMethod, Ignore]
+ public void UnsignedExtensionsAreIgnored() {
+ Assert.Inconclusive("Not yet implemented.");
+ }
+
private static IEnumerable<string> GetAliases(IDictionary<string, string> extraData) {
Regex regex = new Regex(@"^openid\.ns\.(\w+)");
return from key in extraData.Keys
diff --git a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/OpenIdChannelTests.cs b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/OpenIdChannelTests.cs
index f47dfdf..a548969 100644
--- a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/OpenIdChannelTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/OpenIdChannelTests.cs
@@ -1,102 +1,102 @@
-//-----------------------------------------------------------------------
-// <copyright file="OpenIdChannelTests.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.OpenId.ChannelElements {
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Bindings;
- using DotNetOpenAuth.Messaging.Reflection;
- using DotNetOpenAuth.OpenId;
- using DotNetOpenAuth.OpenId.ChannelElements;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
-
- [TestClass]
- public class OpenIdChannelTests : TestBase {
- private static readonly TimeSpan maximumMessageAge = TimeSpan.FromHours(3); // good for tests, too long for production
- private OpenIdChannel channel;
- private OpenIdChannel_Accessor accessor;
- private Mocks.TestWebRequestHandler webHandler;
-
- [TestInitialize]
- public void Setup() {
- this.webHandler = new Mocks.TestWebRequestHandler();
- this.channel = new OpenIdChannel(new AssociationMemoryStore<Uri>(), new NonceMemoryStore(maximumMessageAge));
- this.accessor = OpenIdChannel_Accessor.AttachShadow(this.channel);
- this.channel.WebRequestHandler = this.webHandler;
- }
-
- [TestMethod]
- public void Ctor() {
- // Verify that the channel stack includes the expected types.
- // While other binding elements may be substituted for these, we'd then have
- // to test them. Since we're not testing them in the OpenID battery of tests,
- // we make sure they are the standard ones so that we trust they are tested
- // elsewhere by the testing library.
- var replayElement = (StandardReplayProtectionBindingElement)this.channel.BindingElements.SingleOrDefault(el => el is StandardReplayProtectionBindingElement);
- Assert.IsTrue(this.channel.BindingElements.Any(el => el is StandardExpirationBindingElement));
- Assert.IsNotNull(replayElement);
-
- // Verify that empty nonces are allowed, since OpenID 2.0 allows this.
- Assert.IsTrue(replayElement.AllowZeroLengthNonce);
- }
-
- /// <summary>
- /// Verifies that the channel sends direct message requests as HTTP POST requests.
- /// </summary>
- [TestMethod]
- public void DirectRequestsUsePost() {
- IDirectedProtocolMessage requestMessage = new Mocks.TestDirectedMessage(MessageTransport.Direct) {
- Recipient = new Uri("http://host"),
- Name = "Andrew",
- };
- HttpWebRequest httpRequest = this.accessor.CreateHttpRequest(requestMessage);
- Assert.AreEqual("POST", httpRequest.Method);
- StringAssert.Contains(this.webHandler.RequestEntityAsString, "Name=Andrew");
- }
-
- /// <summary>
- /// Verifies that direct response messages are encoded using Key Value Form.
- /// </summary>
- /// <remarks>
- /// The validity of the actual KVF encoding is not checked here. We assume that the KVF encoding
- /// class is verified elsewhere. We're only checking that the KVF class is being used by the
- /// <see cref="OpenIdChannel.SendDirectMessageResponse"/> method.
- /// </remarks>
- [TestMethod]
- public void DirectResponsesSentUsingKeyValueForm() {
- IProtocolMessage message = MessagingTestBase.GetStandardTestMessage(MessagingTestBase.FieldFill.AllRequired);
- MessageDictionary messageFields = new MessageDictionary(message);
- byte[] expectedBytes = KeyValueFormEncoding.GetBytes(messageFields);
- string expectedContentType = OpenIdChannel_Accessor.KeyValueFormContentType;
-
- UserAgentResponse directResponse = this.accessor.SendDirectMessageResponse(message);
- Assert.AreEqual(expectedContentType, directResponse.Headers[HttpResponseHeader.ContentType]);
- byte[] actualBytes = new byte[directResponse.ResponseStream.Length];
- directResponse.ResponseStream.Read(actualBytes, 0, actualBytes.Length);
- Assert.IsTrue(MessagingUtilities.AreEquivalent(expectedBytes, actualBytes));
- }
-
- /// <summary>
- /// Verifies that direct message responses are read in using the Key Value Form decoder.
- /// </summary>
- [TestMethod]
- public void DirectResponsesReceivedAsKeyValueForm() {
- var fields = new Dictionary<string, string> {
- { "var1", "value1" },
- { "var2", "value2" },
- };
- var response = new DirectWebResponse {
- ResponseStream = new MemoryStream(KeyValueFormEncoding.GetBytes(fields)),
- };
- Assert.IsTrue(MessagingUtilities.AreEquivalent(fields, this.accessor.ReadFromResponseInternal(response)));
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="OpenIdChannelTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Net;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.Messaging.Reflection;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.ChannelElements;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class OpenIdChannelTests : TestBase {
+ private static readonly TimeSpan maximumMessageAge = TimeSpan.FromHours(3); // good for tests, too long for production
+ private OpenIdChannel channel;
+ private OpenIdChannel_Accessor accessor;
+ private Mocks.TestWebRequestHandler webHandler;
+
+ [TestInitialize]
+ public void Setup() {
+ this.webHandler = new Mocks.TestWebRequestHandler();
+ this.channel = new OpenIdChannel(new AssociationMemoryStore<Uri>(), new NonceMemoryStore(maximumMessageAge), new PrivateSecretMemoryStore());
+ this.accessor = OpenIdChannel_Accessor.AttachShadow(this.channel);
+ this.channel.WebRequestHandler = this.webHandler;
+ }
+
+ [TestMethod]
+ public void Ctor() {
+ // Verify that the channel stack includes the expected types.
+ // While other binding elements may be substituted for these, we'd then have
+ // to test them. Since we're not testing them in the OpenID battery of tests,
+ // we make sure they are the standard ones so that we trust they are tested
+ // elsewhere by the testing library.
+ var replayElement = (StandardReplayProtectionBindingElement)this.channel.BindingElements.SingleOrDefault(el => el is StandardReplayProtectionBindingElement);
+ Assert.IsTrue(this.channel.BindingElements.Any(el => el is StandardExpirationBindingElement));
+ Assert.IsNotNull(replayElement);
+
+ // Verify that empty nonces are allowed, since OpenID 2.0 allows this.
+ Assert.IsTrue(replayElement.AllowZeroLengthNonce);
+ }
+
+ /// <summary>
+ /// Verifies that the channel sends direct message requests as HTTP POST requests.
+ /// </summary>
+ [TestMethod]
+ public void DirectRequestsUsePost() {
+ IDirectedProtocolMessage requestMessage = new Mocks.TestDirectedMessage(MessageTransport.Direct) {
+ Recipient = new Uri("http://host"),
+ Name = "Andrew",
+ };
+ HttpWebRequest httpRequest = this.accessor.CreateHttpRequest(requestMessage);
+ Assert.AreEqual("POST", httpRequest.Method);
+ StringAssert.Contains(this.webHandler.RequestEntityAsString, "Name=Andrew");
+ }
+
+ /// <summary>
+ /// Verifies that direct response messages are encoded using Key Value Form.
+ /// </summary>
+ /// <remarks>
+ /// The validity of the actual KVF encoding is not checked here. We assume that the KVF encoding
+ /// class is verified elsewhere. We're only checking that the KVF class is being used by the
+ /// <see cref="OpenIdChannel.SendDirectMessageResponse"/> method.
+ /// </remarks>
+ [TestMethod]
+ public void DirectResponsesSentUsingKeyValueForm() {
+ IProtocolMessage message = MessagingTestBase.GetStandardTestMessage(MessagingTestBase.FieldFill.AllRequired);
+ MessageDictionary messageFields = new MessageDictionary(message);
+ byte[] expectedBytes = KeyValueFormEncoding.GetBytes(messageFields);
+ string expectedContentType = OpenIdChannel_Accessor.KeyValueFormContentType;
+
+ UserAgentResponse directResponse = this.accessor.SendDirectMessageResponse(message);
+ Assert.AreEqual(expectedContentType, directResponse.Headers[HttpResponseHeader.ContentType]);
+ byte[] actualBytes = new byte[directResponse.ResponseStream.Length];
+ directResponse.ResponseStream.Read(actualBytes, 0, actualBytes.Length);
+ Assert.IsTrue(MessagingUtilities.AreEquivalent(expectedBytes, actualBytes));
+ }
+
+ /// <summary>
+ /// Verifies that direct message responses are read in using the Key Value Form decoder.
+ /// </summary>
+ [TestMethod]
+ public void DirectResponsesReceivedAsKeyValueForm() {
+ var fields = new Dictionary<string, string> {
+ { "var1", "value1" },
+ { "var2", "value2" },
+ };
+ var response = new DirectWebResponse {
+ ResponseStream = new MemoryStream(KeyValueFormEncoding.GetBytes(fields)),
+ };
+ Assert.IsTrue(MessagingUtilities.AreEquivalent(fields, this.accessor.ReadFromResponseInternal(response)));
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/OpenIdCoordinator.cs b/src/DotNetOpenAuth.Test/OpenId/OpenIdCoordinator.cs
index 4e74525..3b925a8 100644
--- a/src/DotNetOpenAuth.Test/OpenId/OpenIdCoordinator.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/OpenIdCoordinator.cs
@@ -1,47 +1,47 @@
-//-----------------------------------------------------------------------
-// <copyright file="OpenIdCoordinator.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.OpenId {
- using System;
- using DotNetOpenAuth.Messaging.Bindings;
- using DotNetOpenAuth.OpenId;
- using DotNetOpenAuth.OpenId.Provider;
- using DotNetOpenAuth.OpenId.RelyingParty;
- using DotNetOpenAuth.Test.Mocks;
-
- internal class OpenIdCoordinator : CoordinatorBase<OpenIdRelyingParty, OpenIdProvider> {
- internal OpenIdCoordinator(Action<OpenIdRelyingParty> rpAction, Action<OpenIdProvider> opAction)
- : base(rpAction, opAction) {
- }
-
- internal OpenIdProvider Provider { get; set; }
-
- internal OpenIdRelyingParty RelyingParty { get; set; }
-
- internal override void Run() {
- this.EnsurePartiesAreInitialized();
- var rpCoordinatingChannel = new CoordinatingChannel(this.RelyingParty.Channel, this.IncomingMessageFilter, this.OutgoingMessageFilter);
- var opCoordinatingChannel = new CoordinatingChannel(this.Provider.Channel, this.IncomingMessageFilter, this.OutgoingMessageFilter);
- rpCoordinatingChannel.RemoteChannel = opCoordinatingChannel;
- opCoordinatingChannel.RemoteChannel = rpCoordinatingChannel;
-
- this.RelyingParty.Channel = rpCoordinatingChannel;
- this.Provider.Channel = opCoordinatingChannel;
-
- RunCore(this.RelyingParty, this.Provider);
- }
-
- private void EnsurePartiesAreInitialized() {
- if (this.RelyingParty == null) {
- this.RelyingParty = new OpenIdRelyingParty(new AssociationMemoryStore<Uri>(), new NonceMemoryStore(TimeSpan.FromHours(3)));
- }
-
- if (this.Provider == null) {
- this.Provider = new OpenIdProvider(new AssociationMemoryStore<AssociationRelyingPartyType>(), new NonceMemoryStore(TimeSpan.FromHours(3)));
- }
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="OpenIdCoordinator.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId {
+ using System;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.Provider;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+ using DotNetOpenAuth.Test.Mocks;
+
+ internal class OpenIdCoordinator : CoordinatorBase<OpenIdRelyingParty, OpenIdProvider> {
+ internal OpenIdCoordinator(Action<OpenIdRelyingParty> rpAction, Action<OpenIdProvider> opAction)
+ : base(rpAction, opAction) {
+ }
+
+ internal OpenIdProvider Provider { get; set; }
+
+ internal OpenIdRelyingParty RelyingParty { get; set; }
+
+ internal override void Run() {
+ this.EnsurePartiesAreInitialized();
+ var rpCoordinatingChannel = new CoordinatingChannel(this.RelyingParty.Channel, this.IncomingMessageFilter, this.OutgoingMessageFilter);
+ var opCoordinatingChannel = new CoordinatingChannel(this.Provider.Channel, this.IncomingMessageFilter, this.OutgoingMessageFilter);
+ rpCoordinatingChannel.RemoteChannel = opCoordinatingChannel;
+ opCoordinatingChannel.RemoteChannel = rpCoordinatingChannel;
+
+ this.RelyingParty.Channel = rpCoordinatingChannel;
+ this.Provider.Channel = opCoordinatingChannel;
+
+ RunCore(this.RelyingParty, this.Provider);
+ }
+
+ private void EnsurePartiesAreInitialized() {
+ if (this.RelyingParty == null) {
+ this.RelyingParty = new OpenIdRelyingParty(new AssociationMemoryStore<Uri>(), new NonceMemoryStore(TimeSpan.FromHours(3)), new PrivateSecretMemoryStore());
+ }
+
+ if (this.Provider == null) {
+ this.Provider = new OpenIdProvider(new AssociationMemoryStore<AssociationRelyingPartyType>(), new NonceMemoryStore(TimeSpan.FromHours(3)));
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs
index c68ba0c..d53810a 100644
--- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/OpenIdRelyingPartyTests.cs
@@ -1,35 +1,35 @@
-//-----------------------------------------------------------------------
-// <copyright file="OpenIdRelyingPartyTests.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging.Bindings;
- using DotNetOpenAuth.OpenId;
- using DotNetOpenAuth.OpenId.RelyingParty;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
-
- [TestClass]
- public class OpenIdRelyingPartyTests : OpenIdTestBase {
- [TestInitialize]
- public override void SetUp() {
- base.SetUp();
- }
-
- [TestMethod, Ignore] // ignored, pending work to make dumb mode a supported scenario.
- public void CtorNullAssociationStore() {
- new OpenIdRelyingParty(null, null);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void SecuritySettingsSetNull() {
- var rp = new OpenIdRelyingParty(new AssociationMemoryStore<Uri>(), new NonceMemoryStore(TimeSpan.FromMinutes(5)));
- rp.SecuritySettings = null;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="OpenIdRelyingPartyTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class OpenIdRelyingPartyTests : OpenIdTestBase {
+ [TestInitialize]
+ public override void SetUp() {
+ base.SetUp();
+ }
+
+ [TestMethod, Ignore] // ignored, pending work to make dumb mode a supported scenario.
+ public void CtorNullAssociationStore() {
+ new OpenIdRelyingParty(null, null, null);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void SecuritySettingsSetNull() {
+ var rp = new OpenIdRelyingParty(new AssociationMemoryStore<Uri>(), new NonceMemoryStore(TimeSpan.FromMinutes(5)), new PrivateSecretMemoryStore());
+ rp.SecuritySettings = null;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
index fe63a50..c01a923 100644
--- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj
+++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
@@ -162,14 +162,18 @@
<Compile Include="OpenId\Association.cs" />
<Compile Include="OpenId\AssociationMemoryStore.cs" />
<Compile Include="OpenId\Associations.cs" />
+ <Compile Include="OpenId\ChannelElements\BackwardCompatibilityBindingElement.cs" />
<Compile Include="OpenId\ChannelElements\ExtensionsBindingElement.cs" />
<Compile Include="OpenId\ChannelElements\IOpenIdExtensionFactory.cs" />
+ <Compile Include="OpenId\ChannelElements\IPrivateSecretStore.cs" />
<Compile Include="OpenId\ChannelElements\ITamperResistantOpenIdMessage.cs" />
<Compile Include="OpenId\ChannelElements\OriginalStringUriEncoder.cs" />
+ <Compile Include="OpenId\ChannelElements\ReturnToNonceBindingElement.cs" />
<Compile Include="OpenId\ChannelElements\SigningBindingElement.cs" />
<Compile Include="OpenId\ChannelElements\KeyValueFormEncoding.cs" />
<Compile Include="OpenId\ChannelElements\OpenIdChannel.cs" />
<Compile Include="OpenId\ChannelElements\OpenIdMessageFactory.cs" />
+ <Compile Include="OpenId\ChannelElements\ReturnToSignatureBindingElement.cs" />
<Compile Include="OpenId\Configuration.cs" />
<Compile Include="OpenId\Extensions\AliasManager.cs" />
<Compile Include="OpenId\Extensions\ExtensionBase.cs" />
@@ -193,6 +197,7 @@
<Compile Include="OpenId\Messages\SignedResponseRequest.cs" />
<Compile Include="OpenId\NoDiscoveryIdentifier.cs" />
<Compile Include="OpenId\OpenIdUtilities.cs" />
+ <Compile Include="OpenId\PrivateSecretMemoryStore.cs" />
<Compile Include="OpenId\Realm.cs" />
<Compile Include="OpenId\RelyingPartyDescription.cs" />
<Compile Include="OpenId\DiffieHellmanUtilities.cs" />
diff --git a/src/DotNetOpenAuth/Messaging/Bindings/ExpiredMessageException.cs b/src/DotNetOpenAuth/Messaging/Bindings/ExpiredMessageException.cs
index fe5bf3a..57d781d 100644
--- a/src/DotNetOpenAuth/Messaging/Bindings/ExpiredMessageException.cs
+++ b/src/DotNetOpenAuth/Messaging/Bindings/ExpiredMessageException.cs
@@ -1,37 +1,37 @@
-//-----------------------------------------------------------------------
-// <copyright file="ExpiredMessageException.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging.Bindings {
- using System;
- using System.Globalization;
-
- /// <summary>
- /// An exception thrown when a message is received that exceeds the maximum message age limit.
- /// </summary>
- [Serializable]
- internal class ExpiredMessageException : ProtocolException {
- /// <summary>
- /// Initializes a new instance of the <see cref="ExpiredMessageException"/> class.
- /// </summary>
- /// <param name="utcExpirationDate">The date the message expired.</param>
- /// <param name="faultedMessage">The expired message.</param>
- public ExpiredMessageException(DateTime utcExpirationDate, IProtocolMessage faultedMessage)
- : base(string.Format(CultureInfo.CurrentCulture, MessagingStrings.ExpiredMessage, utcExpirationDate.ToUniversalTime(), DateTime.UtcNow), faultedMessage) {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ExpiredMessageException"/> class.
- /// </summary>
- /// <param name="info">The <see cref="System.Runtime.Serialization.SerializationInfo"/>
- /// that holds the serialized object data about the exception being thrown.</param>
- /// <param name="context">The System.Runtime.Serialization.StreamingContext
- /// that contains contextual information about the source or destination.</param>
- protected ExpiredMessageException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context)
- : base(info, context) { }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ExpiredMessageException.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging.Bindings {
+ using System;
+ using System.Globalization;
+
+ /// <summary>
+ /// An exception thrown when a message is received that exceeds the maximum message age limit.
+ /// </summary>
+ [Serializable]
+ internal class ExpiredMessageException : ProtocolException {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ExpiredMessageException"/> class.
+ /// </summary>
+ /// <param name="utcExpirationDate">The date the message expired.</param>
+ /// <param name="faultedMessage">The expired message.</param>
+ public ExpiredMessageException(DateTime utcExpirationDate, IProtocolMessage faultedMessage)
+ : base(string.Format(CultureInfo.CurrentCulture, MessagingStrings.ExpiredMessage, utcExpirationDate.ToLocalTime(), DateTime.Now), faultedMessage) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ExpiredMessageException"/> class.
+ /// </summary>
+ /// <param name="info">The <see cref="System.Runtime.Serialization.SerializationInfo"/>
+ /// that holds the serialized object data about the exception being thrown.</param>
+ /// <param name="context">The System.Runtime.Serialization.StreamingContext
+ /// that contains contextual information about the source or destination.</param>
+ protected ExpiredMessageException(
+ System.Runtime.Serialization.SerializationInfo info,
+ System.Runtime.Serialization.StreamingContext context)
+ : base(info, context) { }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/Bindings/StandardExpirationBindingElement.cs b/src/DotNetOpenAuth/Messaging/Bindings/StandardExpirationBindingElement.cs
index 9bab9db..a00faf0 100644
--- a/src/DotNetOpenAuth/Messaging/Bindings/StandardExpirationBindingElement.cs
+++ b/src/DotNetOpenAuth/Messaging/Bindings/StandardExpirationBindingElement.cs
@@ -1,124 +1,124 @@
-//-----------------------------------------------------------------------
-// <copyright file="StandardExpirationBindingElement.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging.Bindings {
- using System;
-
- /// <summary>
- /// A message expiration enforcing binding element that supports messages
- /// implementing the <see cref="IExpiringProtocolMessage"/> interface.
- /// </summary>
- internal class StandardExpirationBindingElement : IChannelBindingElement {
- /// <summary>
- /// The default maximum message age to use if the default constructor is called.
- /// </summary>
- internal static readonly TimeSpan DefaultMaximumMessageAge = TimeSpan.FromMinutes(13);
-
- /// <summary>
- /// Initializes a new instance of the <see cref="StandardExpirationBindingElement"/> class
- /// with a default maximum message lifetime of 13 minutes.
- /// </summary>
- internal StandardExpirationBindingElement()
- : this(DefaultMaximumMessageAge) {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="StandardExpirationBindingElement"/> class.
- /// </summary>
- /// <param name="maximumAge">
- /// <para>The maximum age a message implementing the
- /// <see cref="IExpiringProtocolMessage"/> interface can be before
- /// being discarded as too old.</para>
- /// <para>This time limit should take into account expected time skew for servers
- /// across the Internet. For example, if a server could conceivably have its
- /// clock d = 5 minutes off UTC time, then any two servers could have
- /// their clocks disagree by as much as 2*d = 10 minutes.
- /// If a message should live for at least t = 3 minutes,
- /// this property should be set to (2*d + t) = 13 minutes.</para>
- /// </param>
- internal StandardExpirationBindingElement(TimeSpan maximumAge) {
- this.MaximumMessageAge = maximumAge;
- }
-
- #region IChannelBindingElement Properties
-
- /// <summary>
- /// Gets the protection offered by this binding element.
- /// </summary>
- /// <value><see cref="MessageProtections.Expiration"/></value>
- MessageProtections IChannelBindingElement.Protection {
- get { return MessageProtections.Expiration; }
- }
-
- /// <summary>
- /// Gets or sets the channel that this binding element belongs to.
- /// </summary>
- public Channel Channel { get; set; }
-
- #endregion
-
- /// <summary>
- /// Gets the maximum age a message implementing the
- /// <see cref="IExpiringProtocolMessage"/> interface can be before
- /// being discarded as too old.
- /// </summary>
- protected internal TimeSpan MaximumMessageAge {
- get;
- private set;
- }
-
- #region IChannelBindingElement Methods
-
- /// <summary>
- /// Sets the timestamp on an outgoing message.
- /// </summary>
- /// <param name="message">The outgoing message.</param>
- /// <returns>
- /// True if the <paramref name="message"/> applied to this binding element
- /// and the operation was successful. False otherwise.
- /// </returns>
- bool IChannelBindingElement.PrepareMessageForSending(IProtocolMessage message) {
- IExpiringProtocolMessage expiringMessage = message as IExpiringProtocolMessage;
- if (expiringMessage != null) {
- expiringMessage.UtcCreationDate = DateTime.UtcNow;
- return true;
- }
-
- return false;
- }
-
- /// <summary>
- /// Reads the timestamp on a message and throws an exception if the message is too old.
- /// </summary>
- /// <param name="message">The incoming message.</param>
- /// <returns>
- /// True if the <paramref name="message"/> applied to this binding element
- /// and the operation was successful. False if the operation did not apply to this message.
- /// </returns>
- /// <exception cref="ExpiredMessageException">Thrown if the given message has already expired.</exception>
- /// <exception cref="ProtocolException">
- /// Thrown when the binding element rules indicate that this message is invalid and should
- /// NOT be processed.
- /// </exception>
- bool IChannelBindingElement.PrepareMessageForReceiving(IProtocolMessage message) {
- IExpiringProtocolMessage expiringMessage = message as IExpiringProtocolMessage;
- if (expiringMessage != null) {
- // Yes the UtcCreationDate is supposed to always be in UTC already,
- // but just in case a given message failed to guarantee that, we do it here.
- DateTime expirationDate = expiringMessage.UtcCreationDate.ToUniversalTime() + this.MaximumMessageAge;
- if (expirationDate < DateTime.UtcNow) {
- throw new ExpiredMessageException(expirationDate, expiringMessage);
- }
-
- return true;
- }
-
- return false;
- }
-
- #endregion
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="StandardExpirationBindingElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging.Bindings {
+ using System;
+
+ /// <summary>
+ /// A message expiration enforcing binding element that supports messages
+ /// implementing the <see cref="IExpiringProtocolMessage"/> interface.
+ /// </summary>
+ internal class StandardExpirationBindingElement : IChannelBindingElement {
+ /// <summary>
+ /// The default maximum message age to use if the default constructor is called.
+ /// </summary>
+ internal static readonly TimeSpan DefaultMaximumMessageAge = TimeSpan.FromMinutes(13);
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="StandardExpirationBindingElement"/> class
+ /// with a default maximum message lifetime of 13 minutes.
+ /// </summary>
+ internal StandardExpirationBindingElement()
+ : this(DefaultMaximumMessageAge) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="StandardExpirationBindingElement"/> class.
+ /// </summary>
+ /// <param name="maximumAge">
+ /// <para>The maximum age a message implementing the
+ /// <see cref="IExpiringProtocolMessage"/> interface can be before
+ /// being discarded as too old.</para>
+ /// <para>This time limit should take into account expected time skew for servers
+ /// across the Internet. For example, if a server could conceivably have its
+ /// clock d = 5 minutes off UTC time, then any two servers could have
+ /// their clocks disagree by as much as 2*d = 10 minutes.
+ /// If a message should live for at least t = 3 minutes,
+ /// this property should be set to (2*d + t) = 13 minutes.</para>
+ /// </param>
+ internal StandardExpirationBindingElement(TimeSpan maximumAge) {
+ this.MaximumMessageAge = maximumAge;
+ }
+
+ #region IChannelBindingElement Properties
+
+ /// <summary>
+ /// Gets the protection offered by this binding element.
+ /// </summary>
+ /// <value><see cref="MessageProtections.Expiration"/></value>
+ MessageProtections IChannelBindingElement.Protection {
+ get { return MessageProtections.Expiration; }
+ }
+
+ /// <summary>
+ /// Gets or sets the channel that this binding element belongs to.
+ /// </summary>
+ public Channel Channel { get; set; }
+
+ #endregion
+
+ /// <summary>
+ /// Gets the maximum age a message implementing the
+ /// <see cref="IExpiringProtocolMessage"/> interface can be before
+ /// being discarded as too old.
+ /// </summary>
+ protected internal TimeSpan MaximumMessageAge {
+ get;
+ private set;
+ }
+
+ #region IChannelBindingElement Methods
+
+ /// <summary>
+ /// Sets the timestamp on an outgoing message.
+ /// </summary>
+ /// <param name="message">The outgoing message.</param>
+ /// <returns>
+ /// True if the <paramref name="message"/> applied to this binding element
+ /// and the operation was successful. False otherwise.
+ /// </returns>
+ public bool PrepareMessageForSending(IProtocolMessage message) {
+ IExpiringProtocolMessage expiringMessage = message as IExpiringProtocolMessage;
+ if (expiringMessage != null) {
+ expiringMessage.UtcCreationDate = DateTime.UtcNow;
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Reads the timestamp on a message and throws an exception if the message is too old.
+ /// </summary>
+ /// <param name="message">The incoming message.</param>
+ /// <returns>
+ /// True if the <paramref name="message"/> applied to this binding element
+ /// and the operation was successful. False if the operation did not apply to this message.
+ /// </returns>
+ /// <exception cref="ExpiredMessageException">Thrown if the given message has already expired.</exception>
+ /// <exception cref="ProtocolException">
+ /// Thrown when the binding element rules indicate that this message is invalid and should
+ /// NOT be processed.
+ /// </exception>
+ public bool PrepareMessageForReceiving(IProtocolMessage message) {
+ IExpiringProtocolMessage expiringMessage = message as IExpiringProtocolMessage;
+ if (expiringMessage != null) {
+ // Yes the UtcCreationDate is supposed to always be in UTC already,
+ // but just in case a given message failed to guarantee that, we do it here.
+ DateTime expirationDate = expiringMessage.UtcCreationDate.ToUniversalTime() + this.MaximumMessageAge;
+ if (expirationDate < DateTime.UtcNow) {
+ throw new ExpiredMessageException(expirationDate, expiringMessage);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs b/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs
index 942da47..6748a9c 100644
--- a/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs
+++ b/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs
@@ -1,152 +1,152 @@
-//-----------------------------------------------------------------------
-// <copyright file="StandardReplayProtectionBindingElement.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging.Bindings {
- using System;
- using System.Diagnostics;
-
- /// <summary>
- /// A binding element that checks/verifies a nonce message part.
- /// </summary>
- internal class StandardReplayProtectionBindingElement : IChannelBindingElement {
- /// <summary>
- /// These are the characters that may be chosen from when forming a random nonce.
- /// </summary>
- private const string AllowedCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
-
- /// <summary>
- /// The persistent store for nonces received.
- /// </summary>
- private INonceStore nonceStore;
-
- /// <summary>
- /// The length of generated nonces.
- /// </summary>
- private int nonceLength = 8;
-
- /// <summary>
- /// A random number generator.
- /// </summary>
- private Random generator = new Random();
-
- /// <summary>
- /// Initializes a new instance of the <see cref="StandardReplayProtectionBindingElement"/> class.
- /// </summary>
- /// <param name="nonceStore">The store where nonces will be persisted and checked.</param>
- internal StandardReplayProtectionBindingElement(INonceStore nonceStore)
- : this(nonceStore, false) {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="StandardReplayProtectionBindingElement"/> class.
- /// </summary>
- /// <param name="nonceStore">The store where nonces will be persisted and checked.</param>
- /// <param name="allowEmptyNonces">A value indicating whether zero-length nonces will be allowed.</param>
- internal StandardReplayProtectionBindingElement(INonceStore nonceStore, bool allowEmptyNonces) {
- ErrorUtilities.VerifyArgumentNotNull(nonceStore, "nonceStore");
-
- this.nonceStore = nonceStore;
- this.AllowZeroLengthNonce = allowEmptyNonces;
- }
-
- #region IChannelBindingElement Properties
-
- /// <summary>
- /// Gets the protection that this binding element provides messages.
- /// </summary>
- public MessageProtections Protection {
- get { return MessageProtections.ReplayProtection; }
- }
-
- /// <summary>
- /// Gets or sets the channel that this binding element belongs to.
- /// </summary>
- public Channel Channel { get; set; }
-
- #endregion
-
- /// <summary>
- /// Gets or sets the strength of the nonce, which is measured by the number of
- /// nonces that could theoretically be generated.
- /// </summary>
- /// <remarks>
- /// The strength of the nonce is equal to the number of characters that might appear
- /// in the nonce to the power of the length of the nonce.
- /// </remarks>
- internal double NonceStrength {
- get {
- return Math.Pow(AllowedCharacters.Length, this.nonceLength);
- }
-
- set {
- value = Math.Max(value, AllowedCharacters.Length);
- this.nonceLength = (int)Math.Log(value, AllowedCharacters.Length);
- Debug.Assert(this.nonceLength > 0, "Nonce length calculated to be below 1!");
- }
- }
-
- /// <summary>
- /// Gets or sets a value indicating whether empty nonces are allowed.
- /// </summary>
- /// <value>Default is <c>false</c>.</value>
- internal bool AllowZeroLengthNonce { get; set; }
-
- #region IChannelBindingElement Methods
-
- /// <summary>
- /// Applies a nonce to the message.
- /// </summary>
- /// <param name="message">The message to apply replay protection to.</param>
- /// <returns>True if the message protection was applied. False otherwise.</returns>
- public bool PrepareMessageForSending(IProtocolMessage message) {
- IReplayProtectedProtocolMessage nonceMessage = message as IReplayProtectedProtocolMessage;
- if (nonceMessage != null) {
- nonceMessage.Nonce = this.GenerateUniqueFragment();
- return true;
- }
-
- return false;
- }
-
- /// <summary>
- /// Verifies that the nonce in an incoming message has not been seen before.
- /// </summary>
- /// <param name="message">The incoming message.</param>
- /// <returns>
- /// True if the message nonce passed replay detection checks.
- /// False if the message did not have a nonce that could be checked at all.
- /// </returns>
- /// <exception cref="ReplayedMessageException">Thrown when the nonce check revealed a replayed message.</exception>
- public bool PrepareMessageForReceiving(IProtocolMessage message) {
- IReplayProtectedProtocolMessage nonceMessage = message as IReplayProtectedProtocolMessage;
- if (nonceMessage != null) {
- ErrorUtilities.VerifyProtocol(nonceMessage.Nonce != null && (nonceMessage.Nonce.Length > 0 || this.AllowZeroLengthNonce), MessagingStrings.InvalidNonceReceived);
-
- if (!this.nonceStore.StoreNonce(nonceMessage.Nonce, nonceMessage.UtcCreationDate)) {
- throw new ReplayedMessageException(message);
- }
-
- return true;
- }
-
- return false;
- }
-
- #endregion
-
- /// <summary>
- /// Generates a string of random characters for use as a nonce.
- /// </summary>
- /// <returns>The nonce string.</returns>
- private string GenerateUniqueFragment() {
- char[] nonce = new char[this.nonceLength];
- for (int i = 0; i < nonce.Length; i++) {
- nonce[i] = AllowedCharacters[this.generator.Next(AllowedCharacters.Length)];
- }
- return new string(nonce);
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="StandardReplayProtectionBindingElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging.Bindings {
+ using System;
+ using System.Diagnostics;
+
+ /// <summary>
+ /// A binding element that checks/verifies a nonce message part.
+ /// </summary>
+ internal class StandardReplayProtectionBindingElement : IChannelBindingElement {
+ /// <summary>
+ /// These are the characters that may be chosen from when forming a random nonce.
+ /// </summary>
+ private const string AllowedCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+ /// <summary>
+ /// The persistent store for nonces received.
+ /// </summary>
+ private INonceStore nonceStore;
+
+ /// <summary>
+ /// The length of generated nonces.
+ /// </summary>
+ private int nonceLength = 8;
+
+ /// <summary>
+ /// A random number generator.
+ /// </summary>
+ private Random generator = new Random();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="StandardReplayProtectionBindingElement"/> class.
+ /// </summary>
+ /// <param name="nonceStore">The store where nonces will be persisted and checked.</param>
+ internal StandardReplayProtectionBindingElement(INonceStore nonceStore)
+ : this(nonceStore, false) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="StandardReplayProtectionBindingElement"/> class.
+ /// </summary>
+ /// <param name="nonceStore">The store where nonces will be persisted and checked.</param>
+ /// <param name="allowEmptyNonces">A value indicating whether zero-length nonces will be allowed.</param>
+ internal StandardReplayProtectionBindingElement(INonceStore nonceStore, bool allowEmptyNonces) {
+ ErrorUtilities.VerifyArgumentNotNull(nonceStore, "nonceStore");
+
+ this.nonceStore = nonceStore;
+ this.AllowZeroLengthNonce = allowEmptyNonces;
+ }
+
+ #region IChannelBindingElement Properties
+
+ /// <summary>
+ /// Gets the protection that this binding element provides messages.
+ /// </summary>
+ public MessageProtections Protection {
+ get { return MessageProtections.ReplayProtection; }
+ }
+
+ /// <summary>
+ /// Gets or sets the channel that this binding element belongs to.
+ /// </summary>
+ public Channel Channel { get; set; }
+
+ #endregion
+
+ /// <summary>
+ /// Gets or sets the strength of the nonce, which is measured by the number of
+ /// nonces that could theoretically be generated.
+ /// </summary>
+ /// <remarks>
+ /// The strength of the nonce is equal to the number of characters that might appear
+ /// in the nonce to the power of the length of the nonce.
+ /// </remarks>
+ internal double NonceStrength {
+ get {
+ return Math.Pow(AllowedCharacters.Length, this.nonceLength);
+ }
+
+ set {
+ value = Math.Max(value, AllowedCharacters.Length);
+ this.nonceLength = (int)Math.Log(value, AllowedCharacters.Length);
+ Debug.Assert(this.nonceLength > 0, "Nonce length calculated to be below 1!");
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether empty nonces are allowed.
+ /// </summary>
+ /// <value>Default is <c>false</c>.</value>
+ internal bool AllowZeroLengthNonce { get; set; }
+
+ #region IChannelBindingElement Methods
+
+ /// <summary>
+ /// Applies a nonce to the message.
+ /// </summary>
+ /// <param name="message">The message to apply replay protection to.</param>
+ /// <returns>True if the message protection was applied. False otherwise.</returns>
+ public bool PrepareMessageForSending(IProtocolMessage message) {
+ IReplayProtectedProtocolMessage nonceMessage = message as IReplayProtectedProtocolMessage;
+ if (nonceMessage != null) {
+ nonceMessage.Nonce = this.GenerateUniqueFragment();
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Verifies that the nonce in an incoming message has not been seen before.
+ /// </summary>
+ /// <param name="message">The incoming message.</param>
+ /// <returns>
+ /// True if the message nonce passed replay detection checks.
+ /// False if the message did not have a nonce that could be checked at all.
+ /// </returns>
+ /// <exception cref="ReplayedMessageException">Thrown when the nonce check revealed a replayed message.</exception>
+ public bool PrepareMessageForReceiving(IProtocolMessage message) {
+ IReplayProtectedProtocolMessage nonceMessage = message as IReplayProtectedProtocolMessage;
+ if (nonceMessage != null && nonceMessage.Nonce != null) {
+ ErrorUtilities.VerifyProtocol(nonceMessage.Nonce.Length > 0 || this.AllowZeroLengthNonce, MessagingStrings.InvalidNonceReceived);
+
+ if (!this.nonceStore.StoreNonce(nonceMessage.Nonce, nonceMessage.UtcCreationDate)) {
+ throw new ReplayedMessageException(message);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Generates a string of random characters for use as a nonce.
+ /// </summary>
+ /// <returns>The nonce string.</returns>
+ private string GenerateUniqueFragment() {
+ char[] nonce = new char[this.nonceLength];
+ for (int i = 0; i < nonce.Length; i++) {
+ nonce[i] = AllowedCharacters[this.generator.Next(AllowedCharacters.Length)];
+ }
+ return new string(nonce);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/Channel.cs b/src/DotNetOpenAuth/Messaging/Channel.cs
index 9ac65f8..914abf2 100644
--- a/src/DotNetOpenAuth/Messaging/Channel.cs
+++ b/src/DotNetOpenAuth/Messaging/Channel.cs
@@ -58,11 +58,13 @@ namespace DotNetOpenAuth.Messaging {
/// <summary>
/// A list of binding elements in the order they must be applied to outgoing messages.
/// </summary>
- /// <remarks>
- /// Incoming messages should have the binding elements applied in reverse order.
- /// </remarks>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
- private List<IChannelBindingElement> bindingElements = new List<IChannelBindingElement>();
+ private List<IChannelBindingElement> outgoingBindingElements = new List<IChannelBindingElement>();
+
+ /// <summary>
+ /// A list of binding elements in the order they must be applied to incoming messages.
+ /// </summary>
+ private List<IChannelBindingElement> incomingBindingElements = new List<IChannelBindingElement>();
/// <summary>
/// Initializes a new instance of the <see cref="Channel"/> class.
@@ -79,9 +81,11 @@ namespace DotNetOpenAuth.Messaging {
this.messageTypeProvider = messageTypeProvider;
this.WebRequestHandler = new StandardWebRequestHandler();
- this.bindingElements = new List<IChannelBindingElement>(ValidateAndPrepareBindingElements(bindingElements));
+ this.outgoingBindingElements = new List<IChannelBindingElement>(ValidateAndPrepareBindingElements(bindingElements));
+ this.incomingBindingElements = new List<IChannelBindingElement>(this.outgoingBindingElements);
+ this.incomingBindingElements.Reverse();
- foreach (var element in this.bindingElements) {
+ foreach (var element in this.outgoingBindingElements) {
element.Channel = this;
}
}
@@ -102,15 +106,24 @@ namespace DotNetOpenAuth.Messaging {
public IDirectWebRequestHandler WebRequestHandler { get; set; }
/// <summary>
- /// Gets the binding elements used by this channel, in the order they are applied to outgoing messages.
+ /// Gets the binding elements used by this channel, in no particular guaranteed order.
/// </summary>
- /// <remarks>
- /// Incoming messages are processed by this binding elements in the reverse order.
- /// </remarks>
protected internal ReadOnlyCollection<IChannelBindingElement> BindingElements {
- get {
- return this.bindingElements.AsReadOnly();
- }
+ get { return this.outgoingBindingElements.AsReadOnly(); }
+ }
+
+ /// <summary>
+ /// Gets the binding elements used by this channel, in the order applied to outgoing messages.
+ /// </summary>
+ protected internal ReadOnlyCollection<IChannelBindingElement> OutgoingBindingElements {
+ get { return this.outgoingBindingElements.AsReadOnly(); }
+ }
+
+ /// <summary>
+ /// Gets the binding elements used by this channel, in the order applied to incoming messages.
+ /// </summary>
+ protected internal ReadOnlyCollection<IChannelBindingElement> IncomingBindingElements {
+ get { return this.incomingBindingElements.AsReadOnly(); }
}
/// <summary>
@@ -568,12 +581,19 @@ namespace DotNetOpenAuth.Messaging {
throw new ArgumentNullException("message");
}
+ Logger.DebugFormat("Preparing to send {0} ({1}) message.", message.GetType().Name, message.Version);
this.OnSending(message);
MessageProtections appliedProtection = MessageProtections.None;
- foreach (IChannelBindingElement bindingElement in this.bindingElements) {
+ foreach (IChannelBindingElement bindingElement in this.outgoingBindingElements) {
if (bindingElement.PrepareMessageForSending(message)) {
+ Logger.DebugFormat("Binding element {0} applied to message.", bindingElement.GetType().FullName);
+ // Ensure that only one protection binding element applies to this message
+ // for each protection type.
+ ErrorUtilities.VerifyProtocol((appliedProtection & bindingElement.Protection) == 0, MessagingStrings.TooManyBindingsOfferingSameProtection, bindingElement.Protection);
appliedProtection |= bindingElement.Protection;
+ } else {
+ Logger.DebugFormat("Binding element {0} did not apply to message.", bindingElement.GetType().FullName);
}
}
@@ -651,10 +671,18 @@ namespace DotNetOpenAuth.Messaging {
protected virtual void VerifyMessageAfterReceiving(IProtocolMessage message) {
Debug.Assert(message != null, "message == null");
+ Logger.DebugFormat("Preparing to receive {0} ({1}) message.", message.GetType().Name, message.Version);
+
MessageProtections appliedProtection = MessageProtections.None;
- foreach (IChannelBindingElement bindingElement in this.bindingElements.Reverse<IChannelBindingElement>()) {
+ foreach (IChannelBindingElement bindingElement in this.incomingBindingElements) {
if (bindingElement.PrepareMessageForReceiving(message)) {
+ Logger.DebugFormat("Binding element {0} applied to message.", bindingElement.GetType().FullName);
+ // Ensure that only one protection binding element applies to this message
+ // for each protection type.
+ ErrorUtilities.VerifyInternal((appliedProtection & bindingElement.Protection) == 0, MessagingStrings.TooManyBindingsOfferingSameProtection, bindingElement.Protection);
appliedProtection |= bindingElement.Protection;
+ } else {
+ Logger.DebugFormat("Binding element {0} did not apply to message.", bindingElement.GetType().FullName);
}
}
@@ -669,6 +697,35 @@ namespace DotNetOpenAuth.Messaging {
message.EnsureValidMessage();
}
+ protected void CustomizeBindingElementOrder(IEnumerable<IChannelBindingElement> outgoingOrder, IEnumerable<IChannelBindingElement> incomingOrder) {
+ ErrorUtilities.VerifyArgumentNotNull(outgoingOrder, "outgoingOrder");
+ ErrorUtilities.VerifyArgumentNotNull(incomingOrder, "incomingOrder");
+
+ ErrorUtilities.VerifyArgument(IsBindingElementOrderValid(outgoingOrder), MessagingStrings.InvalidCustomBindingElementOrder);
+ ErrorUtilities.VerifyArgument(IsBindingElementOrderValid(incomingOrder), MessagingStrings.InvalidCustomBindingElementOrder);
+
+ this.outgoingBindingElements.Clear();
+ this.outgoingBindingElements.AddRange(outgoingOrder);
+ this.incomingBindingElements.Clear();
+ this.incomingBindingElements.AddRange(incomingOrder);
+ }
+
+ private bool IsBindingElementOrderValid(IEnumerable<IChannelBindingElement> order) {
+ ErrorUtilities.VerifyArgumentNotNull(order, "order");
+
+ // Check that the same number of binding elements are defined.
+ if (order.Count() != this.OutgoingBindingElements.Count) {
+ return false;
+ }
+
+ // Check that every binding element appears exactly once.
+ if (order.Any(el => !this.OutgoingBindingElements.Contains(el))) {
+ return false;
+ }
+
+ return true;
+ }
+
/// <summary>
/// Verifies that all required message parts are initialized to values
/// prior to sending the message to a remote party.
@@ -741,15 +798,6 @@ namespace DotNetOpenAuth.Messaging {
protectionKind));
}
- // At most one binding element for each protection type.
- if (countProtectionsOfThisKind > 1) {
- throw new ProtocolException(
- string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.TooManyBindingsOfferingSameProtection,
- protectionKind,
- countProtectionsOfThisKind));
- }
wasLastProtectionPresent = countProtectionsOfThisKind > 0;
}
diff --git a/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs b/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs
index 4256414..b8213b3 100644
--- a/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs
+++ b/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs
@@ -1,120 +1,133 @@
-//-----------------------------------------------------------------------
-// <copyright file="ErrorUtilities.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Globalization;
-
- /// <summary>
- /// A collection of error checking and reporting methods.
- /// </summary>
- internal class ErrorUtilities {
- /// <summary>
- /// Wraps an exception in a new <see cref="ProtocolException"/>.
- /// </summary>
- /// <param name="inner">The inner exception to wrap.</param>
- /// <param name="errorMessage">The error message for the outer exception.</param>
- /// <param name="args">The string formatting arguments, if any.</param>
- /// <returns>The newly constructed (unthrown) exception.</returns>
- internal static Exception Wrap(Exception inner, string errorMessage, params object[] args) {
- return new ProtocolException(string.Format(CultureInfo.CurrentCulture, errorMessage, args), inner);
- }
-
- /// <summary>
- /// Checks a condition and throws an internal error exception if it evaluates to false.
- /// </summary>
- /// <param name="condition">The condition to check.</param>
- /// <param name="errorMessage">The message to include in the exception, if created.</param>
- internal static void VerifyInternal(bool condition, string errorMessage) {
- if (!condition) {
- throw new InternalErrorException(errorMessage);
- }
- }
-
- /// <summary>
- /// Throws a <see cref="ProtocolException"/> if some <paramref name="condition"/> evaluates to false.
- /// </summary>
- /// <param name="condition">True to do nothing; false to throw the exception.</param>
- /// <param name="faultedMessage">The message being processed that would be responsible for the exception if thrown.</param>
- /// <param name="errorMessage">The error message for the exception.</param>
- /// <param name="args">The string formatting arguments, if any.</param>
- internal static void VerifyProtocol(bool condition, IProtocolMessage faultedMessage, string errorMessage, params object[] args) {
- if (!condition) {
- throw new ProtocolException(string.Format(CultureInfo.CurrentCulture, errorMessage, args), faultedMessage);
- }
- }
-
- /// <summary>
- /// Throws a <see cref="ProtocolException"/> if some <paramref name="condition"/> evaluates to false.
- /// </summary>
- /// <param name="condition">True to do nothing; false to throw the exception.</param>
- /// <param name="message">The error message for the exception.</param>
- /// <param name="args">The string formatting arguments, if any.</param>
- internal static void VerifyProtocol(bool condition, string message, params object[] args) {
- if (!condition) {
- throw new ProtocolException(string.Format(
- CultureInfo.CurrentCulture,
- message,
- args));
- }
- }
-
- /// <summary>
- /// Throws a <see cref="ProtocolException"/>.
- /// </summary>
- /// <param name="message">The message to set in the exception.</param>
- /// <param name="args">The formatting arguments of the message.</param>
- /// <returns>
- /// An InternalErrorException, which may be "thrown" by the caller in order
- /// to satisfy C# rules to show that code will never be reached, but no value
- /// actually is ever returned because this method guarantees to throw.
- /// </returns>
- internal static Exception ThrowProtocol(string message, params object[] args) {
- VerifyProtocol(false, message, args);
-
- // we never reach here, but this allows callers to "throw" this method.
- return new InternalErrorException();
- }
-
- /// <summary>
- /// Verifies something about the argument supplied to a method.
- /// </summary>
- /// <param name="condition">The condition that must evaluate to true to avoid an exception.</param>
- /// <param name="message">The message to use in the exception if the condition is false.</param>
- /// <param name="args">The string formatting arguments, if any.</param>
- internal static void VerifyArgument(bool condition, string message, params object[] args) {
- if (!condition) {
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, message, args));
- }
- }
-
- /// <summary>
- /// Verifies that some given value is not null.
- /// </summary>
- /// <param name="value">The value to check.</param>
- /// <param name="paramName">Name of the parameter, which will be used in the <see cref="ArgumentException"/>, if thrown.</param>
- /// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is null.</exception>
- internal static void VerifyArgumentNotNull(object value, string paramName) {
- if (Object.ReferenceEquals(value, null)) {
- throw new ArgumentNullException(paramName);
- }
- }
-
- /// <summary>
- /// Verifies that some string is not null and has non-zero length.
- /// </summary>
- /// <param name="value">The value to check.</param>
- /// <param name="paramName">Name of the parameter, which will be used in the <see cref="ArgumentException"/>, if thrown.</param>
- /// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is null.</exception>
- /// <exception cref="ArgumentException">Thrown if <paramref name="value"/> has zero length.</exception>
- internal static void VerifyNonZeroLength(string value, string paramName) {
- VerifyArgumentNotNull(value, paramName);
- if (value.Length == 0) {
- throw new ArgumentException(MessagingStrings.UnexpectedEmptyString, paramName);
- }
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ErrorUtilities.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Globalization;
+
+ /// <summary>
+ /// A collection of error checking and reporting methods.
+ /// </summary>
+ internal class ErrorUtilities {
+ /// <summary>
+ /// Wraps an exception in a new <see cref="ProtocolException"/>.
+ /// </summary>
+ /// <param name="inner">The inner exception to wrap.</param>
+ /// <param name="errorMessage">The error message for the outer exception.</param>
+ /// <param name="args">The string formatting arguments, if any.</param>
+ /// <returns>The newly constructed (unthrown) exception.</returns>
+ internal static Exception Wrap(Exception inner, string errorMessage, params object[] args) {
+ return new ProtocolException(string.Format(CultureInfo.CurrentCulture, errorMessage, args), inner);
+ }
+
+ /// <summary>
+ /// Checks a condition and throws an internal error exception if it evaluates to false.
+ /// </summary>
+ /// <param name="condition">The condition to check.</param>
+ /// <param name="errorMessage">The message to include in the exception, if created.</param>
+ internal static void VerifyInternal(bool condition, string errorMessage) {
+ if (!condition) {
+ throw new InternalErrorException(errorMessage);
+ }
+ }
+
+ /// <summary>
+ /// Checks a condition and throws an internal error exception if it evaluates to false.
+ /// </summary>
+ /// <param name="condition">The condition to check.</param>
+ /// <param name="errorMessage">The message to include in the exception, if created.</param>
+ /// <param name="args">The formatting arguments.</param>
+ internal static void VerifyInternal(bool condition, string errorMessage, params object[] args) {
+ if (!condition) {
+ errorMessage = string.Format(CultureInfo.CurrentCulture, errorMessage, args);
+ throw new InternalErrorException(errorMessage);
+ }
+ }
+
+ /// <summary>
+ /// Throws a <see cref="ProtocolException"/> if some <paramref name="condition"/> evaluates to false.
+ /// </summary>
+ /// <param name="condition">True to do nothing; false to throw the exception.</param>
+ /// <param name="faultedMessage">The message being processed that would be responsible for the exception if thrown.</param>
+ /// <param name="errorMessage">The error message for the exception.</param>
+ /// <param name="args">The string formatting arguments, if any.</param>
+ internal static void VerifyProtocol(bool condition, IProtocolMessage faultedMessage, string errorMessage, params object[] args) {
+ if (!condition) {
+ throw new ProtocolException(string.Format(CultureInfo.CurrentCulture, errorMessage, args), faultedMessage);
+ }
+ }
+
+ /// <summary>
+ /// Throws a <see cref="ProtocolException"/> if some <paramref name="condition"/> evaluates to false.
+ /// </summary>
+ /// <param name="condition">True to do nothing; false to throw the exception.</param>
+ /// <param name="message">The error message for the exception.</param>
+ /// <param name="args">The string formatting arguments, if any.</param>
+ internal static void VerifyProtocol(bool condition, string message, params object[] args) {
+ if (!condition) {
+ throw new ProtocolException(string.Format(
+ CultureInfo.CurrentCulture,
+ message,
+ args));
+ }
+ }
+
+ /// <summary>
+ /// Throws a <see cref="ProtocolException"/>.
+ /// </summary>
+ /// <param name="message">The message to set in the exception.</param>
+ /// <param name="args">The formatting arguments of the message.</param>
+ /// <returns>
+ /// An InternalErrorException, which may be "thrown" by the caller in order
+ /// to satisfy C# rules to show that code will never be reached, but no value
+ /// actually is ever returned because this method guarantees to throw.
+ /// </returns>
+ internal static Exception ThrowProtocol(string message, params object[] args) {
+ VerifyProtocol(false, message, args);
+
+ // we never reach here, but this allows callers to "throw" this method.
+ return new InternalErrorException();
+ }
+
+ /// <summary>
+ /// Verifies something about the argument supplied to a method.
+ /// </summary>
+ /// <param name="condition">The condition that must evaluate to true to avoid an exception.</param>
+ /// <param name="message">The message to use in the exception if the condition is false.</param>
+ /// <param name="args">The string formatting arguments, if any.</param>
+ internal static void VerifyArgument(bool condition, string message, params object[] args) {
+ if (!condition) {
+ throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, message, args));
+ }
+ }
+
+ /// <summary>
+ /// Verifies that some given value is not null.
+ /// </summary>
+ /// <param name="value">The value to check.</param>
+ /// <param name="paramName">Name of the parameter, which will be used in the <see cref="ArgumentException"/>, if thrown.</param>
+ /// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is null.</exception>
+ internal static void VerifyArgumentNotNull(object value, string paramName) {
+ if (Object.ReferenceEquals(value, null)) {
+ throw new ArgumentNullException(paramName);
+ }
+ }
+
+ /// <summary>
+ /// Verifies that some string is not null and has non-zero length.
+ /// </summary>
+ /// <param name="value">The value to check.</param>
+ /// <param name="paramName">Name of the parameter, which will be used in the <see cref="ArgumentException"/>, if thrown.</param>
+ /// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is null.</exception>
+ /// <exception cref="ArgumentException">Thrown if <paramref name="value"/> has zero length.</exception>
+ internal static void VerifyNonZeroLength(string value, string paramName) {
+ VerifyArgumentNotNull(value, paramName);
+ if (value.Length == 0) {
+ throw new ArgumentException(MessagingStrings.UnexpectedEmptyString, paramName);
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs
index be1c673..7d42c00 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs
@@ -1,495 +1,513 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-// This code was generated by a tool.
-// Runtime Version:2.0.50727.3521
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
-
-
- /// <summary>
- /// A strongly-typed resource class, for looking up localized strings, etc.
- /// </summary>
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class MessagingStrings {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal MessagingStrings() {
- }
-
- /// <summary>
- /// Returns the cached ResourceManager instance used by this class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DotNetOpenAuth.Messaging.MessagingStrings", typeof(MessagingStrings).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- /// <summary>
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- /// </summary>
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Argument&apos;s {0}.{1} property is required but is empty or null..
- /// </summary>
- internal static string ArgumentPropertyMissing {
- get {
- return ResourceManager.GetString("ArgumentPropertyMissing", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to HttpContext.Current is null. There must be an ASP.NET request in process for this operation to succeed..
- /// </summary>
- internal static string CurrentHttpContextRequired {
- get {
- return ResourceManager.GetString("CurrentHttpContextRequired", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to DataContractSerializer could not be initialized on message type {0}. Is it missing a [DataContract] attribute?.
- /// </summary>
- internal static string DataContractMissingFromMessageType {
- get {
- return ResourceManager.GetString("DataContractMissingFromMessageType", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to DataContractSerializer could not be initialized on message type {0} because the DataContractAttribute.Namespace property is not set..
- /// </summary>
- internal static string DataContractMissingNamespace {
- get {
- return ResourceManager.GetString("DataContractMissingNamespace", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to An instance of type {0} was expected, but received unexpected derived type {1}..
- /// </summary>
- internal static string DerivedTypeNotExpected {
- get {
- return ResourceManager.GetString("DerivedTypeNotExpected", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The directed message&apos;s Recipient property must not be null..
- /// </summary>
- internal static string DirectedMessageMissingRecipient {
- get {
- return ResourceManager.GetString("DirectedMessageMissingRecipient", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Error while deserializing message {0}..
- /// </summary>
- internal static string ErrorDeserializingMessage {
- get {
- return ResourceManager.GetString("ErrorDeserializingMessage", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Error occurred while sending a direct message or getting the response..
- /// </summary>
- internal static string ErrorInRequestReplyMessage {
- get {
- return ResourceManager.GetString("ErrorInRequestReplyMessage", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to This exception was not constructed with a root request message that caused it..
- /// </summary>
- internal static string ExceptionNotConstructedForTransit {
- get {
- return ResourceManager.GetString("ExceptionNotConstructedForTransit", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Expected {0} message but received no recognizable message..
- /// </summary>
- internal static string ExpectedMessageNotReceived {
- get {
- return ResourceManager.GetString("ExpectedMessageNotReceived", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The message expired at {0} and it is now {1}..
- /// </summary>
- internal static string ExpiredMessage {
- get {
- return ResourceManager.GetString("ExpiredMessage", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to At least one of GET or POST flags must be present..
- /// </summary>
- internal static string GetOrPostFlagsRequired {
- get {
- return ResourceManager.GetString("GetOrPostFlagsRequired", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to This method requires a current HttpContext. Alternatively, use an overload of this method that allows you to pass in information without an HttpContext..
- /// </summary>
- internal static string HttpContextRequired {
- get {
- return ResourceManager.GetString("HttpContextRequired", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Messages that indicate indirect transport must implement the {0} interface..
- /// </summary>
- internal static string IndirectMessagesMustImplementIDirectedProtocolMessage {
- get {
- return ResourceManager.GetString("IndirectMessagesMustImplementIDirectedProtocolMessage", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Insecure web request for &apos;{0}&apos; aborted due to security requirements demanding HTTPS..
- /// </summary>
- internal static string InsecureWebRequestWithSslRequired {
- get {
- return ResourceManager.GetString("InsecureWebRequestWithSslRequired", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The {0} message required protections {{{1}}} but the channel could only apply {{{2}}}..
- /// </summary>
- internal static string InsufficientMessageProtection {
- get {
- return ResourceManager.GetString("InsufficientMessageProtection", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Some part(s) of the message have invalid values: {0}.
- /// </summary>
- internal static string InvalidMessageParts {
- get {
- return ResourceManager.GetString("InvalidMessageParts", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The incoming message had an invalid or missing nonce..
- /// </summary>
- internal static string InvalidNonceReceived {
- get {
- return ResourceManager.GetString("InvalidNonceReceived", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to An item with the same key has already been added..
- /// </summary>
- internal static string KeyAlreadyExists {
- get {
- return ResourceManager.GetString("KeyAlreadyExists", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The value for {0}.{1} on member {1} was expected to derive from {2} but was {3}..
- /// </summary>
- internal static string MessagePartEncoderWrongType {
- get {
- return ResourceManager.GetString("MessagePartEncoderWrongType", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Error while reading message &apos;{0}&apos; parameter &apos;{1}&apos; with value &apos;{2}&apos;..
- /// </summary>
- internal static string MessagePartReadFailure {
- get {
- return ResourceManager.GetString("MessagePartReadFailure", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Message parameter &apos;{0}&apos; with value &apos;{1}&apos; failed to base64 decode..
- /// </summary>
- internal static string MessagePartValueBase64DecodingFault {
- get {
- return ResourceManager.GetString("MessagePartValueBase64DecodingFault", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Error while preparing message &apos;{0}&apos; parameter &apos;{1}&apos; for sending..
- /// </summary>
- internal static string MessagePartWriteFailure {
- get {
- return ResourceManager.GetString("MessagePartWriteFailure", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to A message response is already queued for sending in the response stream..
- /// </summary>
- internal static string QueuedMessageResponseAlreadyExists {
- get {
- return ResourceManager.GetString("QueuedMessageResponseAlreadyExists", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to This message has already been processed. This could indicate a replay attack in progress..
- /// </summary>
- internal static string ReplayAttackDetected {
- get {
- return ResourceManager.GetString("ReplayAttackDetected", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to This channel does not support replay protection..
- /// </summary>
- internal static string ReplayProtectionNotSupported {
- get {
- return ResourceManager.GetString("ReplayProtectionNotSupported", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The following required non-empty parameters were empty in the {0} message: {1}.
- /// </summary>
- internal static string RequiredNonEmptyParameterWasEmpty {
- get {
- return ResourceManager.GetString("RequiredNonEmptyParameterWasEmpty", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The following required parameters were missing from the {0} message: {1}.
- /// </summary>
- internal static string RequiredParametersMissing {
- get {
- return ResourceManager.GetString("RequiredParametersMissing", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The binding element offering the {0} protection requires other protection that is not provided..
- /// </summary>
- internal static string RequiredProtectionMissing {
- get {
- return ResourceManager.GetString("RequiredProtectionMissing", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The list is empty..
- /// </summary>
- internal static string SequenceContainsNoElements {
- get {
- return ResourceManager.GetString("SequenceContainsNoElements", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The list contains a null element..
- /// </summary>
- internal static string SequenceContainsNullElement {
- get {
- return ResourceManager.GetString("SequenceContainsNullElement", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Message signature was incorrect..
- /// </summary>
- internal static string SignatureInvalid {
- get {
- return ResourceManager.GetString("SignatureInvalid", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to This channel does not support signing messages. To support signing messages, a derived Channel type must override the Sign and IsSignatureValid methods..
- /// </summary>
- internal static string SigningNotSupported {
- get {
- return ResourceManager.GetString("SigningNotSupported", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The stream&apos;s CanRead property returned false..
- /// </summary>
- internal static string StreamUnreadable {
- get {
- return ResourceManager.GetString("StreamUnreadable", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The stream&apos;s CanWrite property returned false..
- /// </summary>
- internal static string StreamUnwritable {
- get {
- return ResourceManager.GetString("StreamUnwritable", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Expected at most 1 binding element offering the {0} protection, but found {1}..
- /// </summary>
- internal static string TooManyBindingsOfferingSameProtection {
- get {
- return ResourceManager.GetString("TooManyBindingsOfferingSameProtection", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The maximum allowable number of redirects were exceeded while requesting &apos;{0}&apos;..
- /// </summary>
- internal static string TooManyRedirects {
- get {
- return ResourceManager.GetString("TooManyRedirects", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The array must not be empty..
- /// </summary>
- internal static string UnexpectedEmptyArray {
- get {
- return ResourceManager.GetString("UnexpectedEmptyArray", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The empty string is not allowed..
- /// </summary>
- internal static string UnexpectedEmptyString {
- get {
- return ResourceManager.GetString("UnexpectedEmptyString", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Message parameter &apos;{0}&apos; had unexpected value &apos;{1}&apos;..
- /// </summary>
- internal static string UnexpectedMessagePartValue {
- get {
- return ResourceManager.GetString("UnexpectedMessagePartValue", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Expected message {0} parameter &apos;{1}&apos; to have value &apos;{2}&apos; but had &apos;{3}&apos; instead..
- /// </summary>
- internal static string UnexpectedMessagePartValueForConstant {
- get {
- return ResourceManager.GetString("UnexpectedMessagePartValueForConstant", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Expected message {0} but received {1} instead..
- /// </summary>
- internal static string UnexpectedMessageReceived {
- get {
- return ResourceManager.GetString("UnexpectedMessageReceived", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Unexpected message type received..
- /// </summary>
- internal static string UnexpectedMessageReceivedOfMany {
- get {
- return ResourceManager.GetString("UnexpectedMessageReceivedOfMany", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The type {0} or a derived type was expected, but {1} was given..
- /// </summary>
- internal static string UnexpectedType {
- get {
- return ResourceManager.GetString("UnexpectedType", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to {0} property has unrecognized value {1}..
- /// </summary>
- internal static string UnrecognizedEnumValue {
- get {
- return ResourceManager.GetString("UnrecognizedEnumValue", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The URL &apos;{0}&apos; is rated unsafe and cannot be requested this way..
- /// </summary>
- internal static string UnsafeWebRequestDetected {
- get {
- return ResourceManager.GetString("UnsafeWebRequestDetected", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Redirects on POST requests that are to untrusted servers is not supported..
- /// </summary>
- internal static string UntrustedRedirectsOnPOSTNotSupported {
- get {
- return ResourceManager.GetString("UntrustedRedirectsOnPOSTNotSupported", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Web request to &apos;{0}&apos; failed..
- /// </summary>
- internal static string WebRequestFailed {
- get {
- return ResourceManager.GetString("WebRequestFailed", resourceCulture);
- }
- }
- }
-}
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.3521
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+
+
+ /// <summary>
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ /// </summary>
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class MessagingStrings {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal MessagingStrings() {
+ }
+
+ /// <summary>
+ /// Returns the cached ResourceManager instance used by this class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DotNetOpenAuth.Messaging.MessagingStrings", typeof(MessagingStrings).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ /// <summary>
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Argument&apos;s {0}.{1} property is required but is empty or null..
+ /// </summary>
+ internal static string ArgumentPropertyMissing {
+ get {
+ return ResourceManager.GetString("ArgumentPropertyMissing", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to HttpContext.Current is null. There must be an ASP.NET request in process for this operation to succeed..
+ /// </summary>
+ internal static string CurrentHttpContextRequired {
+ get {
+ return ResourceManager.GetString("CurrentHttpContextRequired", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to DataContractSerializer could not be initialized on message type {0}. Is it missing a [DataContract] attribute?.
+ /// </summary>
+ internal static string DataContractMissingFromMessageType {
+ get {
+ return ResourceManager.GetString("DataContractMissingFromMessageType", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to DataContractSerializer could not be initialized on message type {0} because the DataContractAttribute.Namespace property is not set..
+ /// </summary>
+ internal static string DataContractMissingNamespace {
+ get {
+ return ResourceManager.GetString("DataContractMissingNamespace", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to An instance of type {0} was expected, but received unexpected derived type {1}..
+ /// </summary>
+ internal static string DerivedTypeNotExpected {
+ get {
+ return ResourceManager.GetString("DerivedTypeNotExpected", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The directed message&apos;s Recipient property must not be null..
+ /// </summary>
+ internal static string DirectedMessageMissingRecipient {
+ get {
+ return ResourceManager.GetString("DirectedMessageMissingRecipient", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Error while deserializing message {0}..
+ /// </summary>
+ internal static string ErrorDeserializingMessage {
+ get {
+ return ResourceManager.GetString("ErrorDeserializingMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Error occurred while sending a direct message or getting the response..
+ /// </summary>
+ internal static string ErrorInRequestReplyMessage {
+ get {
+ return ResourceManager.GetString("ErrorInRequestReplyMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to This exception was not constructed with a root request message that caused it..
+ /// </summary>
+ internal static string ExceptionNotConstructedForTransit {
+ get {
+ return ResourceManager.GetString("ExceptionNotConstructedForTransit", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Expected {0} message but received no recognizable message..
+ /// </summary>
+ internal static string ExpectedMessageNotReceived {
+ get {
+ return ResourceManager.GetString("ExpectedMessageNotReceived", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The message expired at {0} and it is now {1}..
+ /// </summary>
+ internal static string ExpiredMessage {
+ get {
+ return ResourceManager.GetString("ExpiredMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to At least one of GET or POST flags must be present..
+ /// </summary>
+ internal static string GetOrPostFlagsRequired {
+ get {
+ return ResourceManager.GetString("GetOrPostFlagsRequired", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to This method requires a current HttpContext. Alternatively, use an overload of this method that allows you to pass in information without an HttpContext..
+ /// </summary>
+ internal static string HttpContextRequired {
+ get {
+ return ResourceManager.GetString("HttpContextRequired", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Messages that indicate indirect transport must implement the {0} interface..
+ /// </summary>
+ internal static string IndirectMessagesMustImplementIDirectedProtocolMessage {
+ get {
+ return ResourceManager.GetString("IndirectMessagesMustImplementIDirectedProtocolMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Insecure web request for &apos;{0}&apos; aborted due to security requirements demanding HTTPS..
+ /// </summary>
+ internal static string InsecureWebRequestWithSslRequired {
+ get {
+ return ResourceManager.GetString("InsecureWebRequestWithSslRequired", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The {0} message required protections {{{1}}} but the channel could only apply {{{2}}}..
+ /// </summary>
+ internal static string InsufficientMessageProtection {
+ get {
+ return ResourceManager.GetString("InsufficientMessageProtection", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The customized binding element ordering is invalid..
+ /// </summary>
+ internal static string InvalidCustomBindingElementOrder {
+ get {
+ return ResourceManager.GetString("InvalidCustomBindingElementOrder", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Some part(s) of the message have invalid values: {0}.
+ /// </summary>
+ internal static string InvalidMessageParts {
+ get {
+ return ResourceManager.GetString("InvalidMessageParts", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The incoming message had an invalid or missing nonce..
+ /// </summary>
+ internal static string InvalidNonceReceived {
+ get {
+ return ResourceManager.GetString("InvalidNonceReceived", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to An item with the same key has already been added..
+ /// </summary>
+ internal static string KeyAlreadyExists {
+ get {
+ return ResourceManager.GetString("KeyAlreadyExists", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The value for {0}.{1} on member {1} was expected to derive from {2} but was {3}..
+ /// </summary>
+ internal static string MessagePartEncoderWrongType {
+ get {
+ return ResourceManager.GetString("MessagePartEncoderWrongType", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Error while reading message &apos;{0}&apos; parameter &apos;{1}&apos; with value &apos;{2}&apos;..
+ /// </summary>
+ internal static string MessagePartReadFailure {
+ get {
+ return ResourceManager.GetString("MessagePartReadFailure", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Message parameter &apos;{0}&apos; with value &apos;{1}&apos; failed to base64 decode..
+ /// </summary>
+ internal static string MessagePartValueBase64DecodingFault {
+ get {
+ return ResourceManager.GetString("MessagePartValueBase64DecodingFault", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Error while preparing message &apos;{0}&apos; parameter &apos;{1}&apos; for sending..
+ /// </summary>
+ internal static string MessagePartWriteFailure {
+ get {
+ return ResourceManager.GetString("MessagePartWriteFailure", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A message response is already queued for sending in the response stream..
+ /// </summary>
+ internal static string QueuedMessageResponseAlreadyExists {
+ get {
+ return ResourceManager.GetString("QueuedMessageResponseAlreadyExists", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to This message has already been processed. This could indicate a replay attack in progress..
+ /// </summary>
+ internal static string ReplayAttackDetected {
+ get {
+ return ResourceManager.GetString("ReplayAttackDetected", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to This channel does not support replay protection..
+ /// </summary>
+ internal static string ReplayProtectionNotSupported {
+ get {
+ return ResourceManager.GetString("ReplayProtectionNotSupported", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The following required non-empty parameters were empty in the {0} message: {1}.
+ /// </summary>
+ internal static string RequiredNonEmptyParameterWasEmpty {
+ get {
+ return ResourceManager.GetString("RequiredNonEmptyParameterWasEmpty", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The following required parameters were missing from the {0} message: {1}.
+ /// </summary>
+ internal static string RequiredParametersMissing {
+ get {
+ return ResourceManager.GetString("RequiredParametersMissing", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The binding element offering the {0} protection requires other protection that is not provided..
+ /// </summary>
+ internal static string RequiredProtectionMissing {
+ get {
+ return ResourceManager.GetString("RequiredProtectionMissing", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The list is empty..
+ /// </summary>
+ internal static string SequenceContainsNoElements {
+ get {
+ return ResourceManager.GetString("SequenceContainsNoElements", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The list contains a null element..
+ /// </summary>
+ internal static string SequenceContainsNullElement {
+ get {
+ return ResourceManager.GetString("SequenceContainsNullElement", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Message signature was incorrect..
+ /// </summary>
+ internal static string SignatureInvalid {
+ get {
+ return ResourceManager.GetString("SignatureInvalid", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to This channel does not support signing messages. To support signing messages, a derived Channel type must override the Sign and IsSignatureValid methods..
+ /// </summary>
+ internal static string SigningNotSupported {
+ get {
+ return ResourceManager.GetString("SigningNotSupported", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The stream&apos;s CanRead property returned false..
+ /// </summary>
+ internal static string StreamUnreadable {
+ get {
+ return ResourceManager.GetString("StreamUnreadable", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The stream&apos;s CanWrite property returned false..
+ /// </summary>
+ internal static string StreamUnwritable {
+ get {
+ return ResourceManager.GetString("StreamUnwritable", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Expected at most 1 binding element to apply the {0} protection, but more than one applied..
+ /// </summary>
+ internal static string TooManyBindingsOfferingSameProtection {
+ get {
+ return ResourceManager.GetString("TooManyBindingsOfferingSameProtection", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The maximum allowable number of redirects were exceeded while requesting &apos;{0}&apos;..
+ /// </summary>
+ internal static string TooManyRedirects {
+ get {
+ return ResourceManager.GetString("TooManyRedirects", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The array must not be empty..
+ /// </summary>
+ internal static string UnexpectedEmptyArray {
+ get {
+ return ResourceManager.GetString("UnexpectedEmptyArray", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The empty string is not allowed..
+ /// </summary>
+ internal static string UnexpectedEmptyString {
+ get {
+ return ResourceManager.GetString("UnexpectedEmptyString", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Message parameter &apos;{0}&apos; had unexpected value &apos;{1}&apos;..
+ /// </summary>
+ internal static string UnexpectedMessagePartValue {
+ get {
+ return ResourceManager.GetString("UnexpectedMessagePartValue", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Expected message {0} parameter &apos;{1}&apos; to have value &apos;{2}&apos; but had &apos;{3}&apos; instead..
+ /// </summary>
+ internal static string UnexpectedMessagePartValueForConstant {
+ get {
+ return ResourceManager.GetString("UnexpectedMessagePartValueForConstant", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Expected message {0} but received {1} instead..
+ /// </summary>
+ internal static string UnexpectedMessageReceived {
+ get {
+ return ResourceManager.GetString("UnexpectedMessageReceived", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Unexpected message type received..
+ /// </summary>
+ internal static string UnexpectedMessageReceivedOfMany {
+ get {
+ return ResourceManager.GetString("UnexpectedMessageReceivedOfMany", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A null value was included and is not allowed..
+ /// </summary>
+ internal static string UnexpectedNullValue {
+ get {
+ return ResourceManager.GetString("UnexpectedNullValue", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The type {0} or a derived type was expected, but {1} was given..
+ /// </summary>
+ internal static string UnexpectedType {
+ get {
+ return ResourceManager.GetString("UnexpectedType", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to {0} property has unrecognized value {1}..
+ /// </summary>
+ internal static string UnrecognizedEnumValue {
+ get {
+ return ResourceManager.GetString("UnrecognizedEnumValue", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The URL &apos;{0}&apos; is rated unsafe and cannot be requested this way..
+ /// </summary>
+ internal static string UnsafeWebRequestDetected {
+ get {
+ return ResourceManager.GetString("UnsafeWebRequestDetected", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Redirects on POST requests that are to untrusted servers is not supported..
+ /// </summary>
+ internal static string UntrustedRedirectsOnPOSTNotSupported {
+ get {
+ return ResourceManager.GetString("UntrustedRedirectsOnPOSTNotSupported", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Web request to &apos;{0}&apos; failed..
+ /// </summary>
+ internal static string WebRequestFailed {
+ get {
+ return ResourceManager.GetString("WebRequestFailed", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx
index 1b17d3f..2dee0fd 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx
+++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx
@@ -1,264 +1,270 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
- <!--
- Microsoft ResX Schema
-
- Version 2.0
-
- The primary goals of this format is to allow a simple XML format
- that is mostly human readable. The generation and parsing of the
- various data types are done through the TypeConverter classes
- associated with the data types.
-
- Example:
-
- ... ado.net/XML headers & schema ...
- <resheader name="resmimetype">text/microsoft-resx</resheader>
- <resheader name="version">2.0</resheader>
- <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
- <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
- <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
- <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
- <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
- <value>[base64 mime encoded serialized .NET Framework object]</value>
- </data>
- <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
- <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
- <comment>This is a comment</comment>
- </data>
-
- There are any number of "resheader" rows that contain simple
- name/value pairs.
-
- Each data row contains a name, and value. The row also contains a
- type or mimetype. Type corresponds to a .NET class that support
- text/value conversion through the TypeConverter architecture.
- Classes that don't support this are serialized and stored with the
- mimetype set.
-
- The mimetype is used for serialized objects, and tells the
- ResXResourceReader how to depersist the object. This is currently not
- extensible. For a given mimetype the value must be set accordingly:
-
- Note - application/x-microsoft.net.object.binary.base64 is the format
- that the ResXResourceWriter will generate, however the reader can
- read any of the formats listed below.
-
- mimetype: application/x-microsoft.net.object.binary.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.soap.base64
- value : The object must be serialized with
- : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
- : and then encoded with base64 encoding.
-
- mimetype: application/x-microsoft.net.object.bytearray.base64
- value : The object must be serialized into a byte array
- : using a System.ComponentModel.TypeConverter
- : and then encoded with base64 encoding.
- -->
- <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
- <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
- <xsd:element name="root" msdata:IsDataSet="true">
- <xsd:complexType>
- <xsd:choice maxOccurs="unbounded">
- <xsd:element name="metadata">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" />
- </xsd:sequence>
- <xsd:attribute name="name" use="required" type="xsd:string" />
- <xsd:attribute name="type" type="xsd:string" />
- <xsd:attribute name="mimetype" type="xsd:string" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="assembly">
- <xsd:complexType>
- <xsd:attribute name="alias" type="xsd:string" />
- <xsd:attribute name="name" type="xsd:string" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="data">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
- <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
- <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
- <xsd:attribute ref="xml:space" />
- </xsd:complexType>
- </xsd:element>
- <xsd:element name="resheader">
- <xsd:complexType>
- <xsd:sequence>
- <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
- </xsd:sequence>
- <xsd:attribute name="name" type="xsd:string" use="required" />
- </xsd:complexType>
- </xsd:element>
- </xsd:choice>
- </xsd:complexType>
- </xsd:element>
- </xsd:schema>
- <resheader name="resmimetype">
- <value>text/microsoft-resx</value>
- </resheader>
- <resheader name="version">
- <value>2.0</value>
- </resheader>
- <resheader name="reader">
- <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <resheader name="writer">
- <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
- </resheader>
- <data name="ArgumentPropertyMissing" xml:space="preserve">
- <value>Argument's {0}.{1} property is required but is empty or null.</value>
- </data>
- <data name="CurrentHttpContextRequired" xml:space="preserve">
- <value>HttpContext.Current is null. There must be an ASP.NET request in process for this operation to succeed.</value>
- </data>
- <data name="DataContractMissingFromMessageType" xml:space="preserve">
- <value>DataContractSerializer could not be initialized on message type {0}. Is it missing a [DataContract] attribute?</value>
- </data>
- <data name="DataContractMissingNamespace" xml:space="preserve">
- <value>DataContractSerializer could not be initialized on message type {0} because the DataContractAttribute.Namespace property is not set.</value>
- </data>
- <data name="DerivedTypeNotExpected" xml:space="preserve">
- <value>An instance of type {0} was expected, but received unexpected derived type {1}.</value>
- </data>
- <data name="DirectedMessageMissingRecipient" xml:space="preserve">
- <value>The directed message's Recipient property must not be null.</value>
- </data>
- <data name="ErrorDeserializingMessage" xml:space="preserve">
- <value>Error while deserializing message {0}.</value>
- </data>
- <data name="ErrorInRequestReplyMessage" xml:space="preserve">
- <value>Error occurred while sending a direct message or getting the response.</value>
- </data>
- <data name="ExceptionNotConstructedForTransit" xml:space="preserve">
- <value>This exception was not constructed with a root request message that caused it.</value>
- </data>
- <data name="ExpectedMessageNotReceived" xml:space="preserve">
- <value>Expected {0} message but received no recognizable message.</value>
- </data>
- <data name="ExpiredMessage" xml:space="preserve">
- <value>The message expired at {0} and it is now {1}.</value>
- </data>
- <data name="GetOrPostFlagsRequired" xml:space="preserve">
- <value>At least one of GET or POST flags must be present.</value>
- </data>
- <data name="HttpContextRequired" xml:space="preserve">
- <value>This method requires a current HttpContext. Alternatively, use an overload of this method that allows you to pass in information without an HttpContext.</value>
- </data>
- <data name="IndirectMessagesMustImplementIDirectedProtocolMessage" xml:space="preserve">
- <value>Messages that indicate indirect transport must implement the {0} interface.</value>
- </data>
- <data name="InsecureWebRequestWithSslRequired" xml:space="preserve">
- <value>Insecure web request for '{0}' aborted due to security requirements demanding HTTPS.</value>
- </data>
- <data name="InsufficientMessageProtection" xml:space="preserve">
- <value>The {0} message required protections {{{1}}} but the channel could only apply {{{2}}}.</value>
- </data>
- <data name="InvalidMessageParts" xml:space="preserve">
- <value>Some part(s) of the message have invalid values: {0}</value>
- </data>
- <data name="InvalidNonceReceived" xml:space="preserve">
- <value>The incoming message had an invalid or missing nonce.</value>
- </data>
- <data name="KeyAlreadyExists" xml:space="preserve">
- <value>An item with the same key has already been added.</value>
- </data>
- <data name="MessagePartEncoderWrongType" xml:space="preserve">
- <value>The value for {0}.{1} on member {1} was expected to derive from {2} but was {3}.</value>
- </data>
- <data name="MessagePartReadFailure" xml:space="preserve">
- <value>Error while reading message '{0}' parameter '{1}' with value '{2}'.</value>
- </data>
- <data name="MessagePartValueBase64DecodingFault" xml:space="preserve">
- <value>Message parameter '{0}' with value '{1}' failed to base64 decode.</value>
- </data>
- <data name="MessagePartWriteFailure" xml:space="preserve">
- <value>Error while preparing message '{0}' parameter '{1}' for sending.</value>
- </data>
- <data name="QueuedMessageResponseAlreadyExists" xml:space="preserve">
- <value>A message response is already queued for sending in the response stream.</value>
- </data>
- <data name="ReplayAttackDetected" xml:space="preserve">
- <value>This message has already been processed. This could indicate a replay attack in progress.</value>
- </data>
- <data name="ReplayProtectionNotSupported" xml:space="preserve">
- <value>This channel does not support replay protection.</value>
- </data>
- <data name="RequiredNonEmptyParameterWasEmpty" xml:space="preserve">
- <value>The following required non-empty parameters were empty in the {0} message: {1}</value>
- </data>
- <data name="RequiredParametersMissing" xml:space="preserve">
- <value>The following required parameters were missing from the {0} message: {1}</value>
- </data>
- <data name="RequiredProtectionMissing" xml:space="preserve">
- <value>The binding element offering the {0} protection requires other protection that is not provided.</value>
- </data>
- <data name="SequenceContainsNoElements" xml:space="preserve">
- <value>The list is empty.</value>
- </data>
- <data name="SequenceContainsNullElement" xml:space="preserve">
- <value>The list contains a null element.</value>
- </data>
- <data name="SignatureInvalid" xml:space="preserve">
- <value>Message signature was incorrect.</value>
- </data>
- <data name="SigningNotSupported" xml:space="preserve">
- <value>This channel does not support signing messages. To support signing messages, a derived Channel type must override the Sign and IsSignatureValid methods.</value>
- </data>
- <data name="StreamUnreadable" xml:space="preserve">
- <value>The stream's CanRead property returned false.</value>
- </data>
- <data name="StreamUnwritable" xml:space="preserve">
- <value>The stream's CanWrite property returned false.</value>
- </data>
- <data name="TooManyBindingsOfferingSameProtection" xml:space="preserve">
- <value>Expected at most 1 binding element offering the {0} protection, but found {1}.</value>
- </data>
- <data name="TooManyRedirects" xml:space="preserve">
- <value>The maximum allowable number of redirects were exceeded while requesting '{0}'.</value>
- </data>
- <data name="UnexpectedEmptyArray" xml:space="preserve">
- <value>The array must not be empty.</value>
- </data>
- <data name="UnexpectedEmptyString" xml:space="preserve">
- <value>The empty string is not allowed.</value>
- </data>
- <data name="UnexpectedMessagePartValue" xml:space="preserve">
- <value>Message parameter '{0}' had unexpected value '{1}'.</value>
- </data>
- <data name="UnexpectedMessagePartValueForConstant" xml:space="preserve">
- <value>Expected message {0} parameter '{1}' to have value '{2}' but had '{3}' instead.</value>
- </data>
- <data name="UnexpectedMessageReceived" xml:space="preserve">
- <value>Expected message {0} but received {1} instead.</value>
- </data>
- <data name="UnexpectedMessageReceivedOfMany" xml:space="preserve">
- <value>Unexpected message type received.</value>
- </data>
- <data name="UnexpectedType" xml:space="preserve">
- <value>The type {0} or a derived type was expected, but {1} was given.</value>
- </data>
- <data name="UnrecognizedEnumValue" xml:space="preserve">
- <value>{0} property has unrecognized value {1}.</value>
- </data>
- <data name="UnsafeWebRequestDetected" xml:space="preserve">
- <value>The URL '{0}' is rated unsafe and cannot be requested this way.</value>
- </data>
- <data name="UntrustedRedirectsOnPOSTNotSupported" xml:space="preserve">
- <value>Redirects on POST requests that are to untrusted servers is not supported.</value>
- </data>
- <data name="WebRequestFailed" xml:space="preserve">
- <value>Web request to '{0}' failed.</value>
- </data>
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="ArgumentPropertyMissing" xml:space="preserve">
+ <value>Argument's {0}.{1} property is required but is empty or null.</value>
+ </data>
+ <data name="CurrentHttpContextRequired" xml:space="preserve">
+ <value>HttpContext.Current is null. There must be an ASP.NET request in process for this operation to succeed.</value>
+ </data>
+ <data name="DataContractMissingFromMessageType" xml:space="preserve">
+ <value>DataContractSerializer could not be initialized on message type {0}. Is it missing a [DataContract] attribute?</value>
+ </data>
+ <data name="DataContractMissingNamespace" xml:space="preserve">
+ <value>DataContractSerializer could not be initialized on message type {0} because the DataContractAttribute.Namespace property is not set.</value>
+ </data>
+ <data name="DerivedTypeNotExpected" xml:space="preserve">
+ <value>An instance of type {0} was expected, but received unexpected derived type {1}.</value>
+ </data>
+ <data name="DirectedMessageMissingRecipient" xml:space="preserve">
+ <value>The directed message's Recipient property must not be null.</value>
+ </data>
+ <data name="ErrorDeserializingMessage" xml:space="preserve">
+ <value>Error while deserializing message {0}.</value>
+ </data>
+ <data name="ErrorInRequestReplyMessage" xml:space="preserve">
+ <value>Error occurred while sending a direct message or getting the response.</value>
+ </data>
+ <data name="ExceptionNotConstructedForTransit" xml:space="preserve">
+ <value>This exception was not constructed with a root request message that caused it.</value>
+ </data>
+ <data name="ExpectedMessageNotReceived" xml:space="preserve">
+ <value>Expected {0} message but received no recognizable message.</value>
+ </data>
+ <data name="ExpiredMessage" xml:space="preserve">
+ <value>The message expired at {0} and it is now {1}.</value>
+ </data>
+ <data name="GetOrPostFlagsRequired" xml:space="preserve">
+ <value>At least one of GET or POST flags must be present.</value>
+ </data>
+ <data name="HttpContextRequired" xml:space="preserve">
+ <value>This method requires a current HttpContext. Alternatively, use an overload of this method that allows you to pass in information without an HttpContext.</value>
+ </data>
+ <data name="IndirectMessagesMustImplementIDirectedProtocolMessage" xml:space="preserve">
+ <value>Messages that indicate indirect transport must implement the {0} interface.</value>
+ </data>
+ <data name="InsecureWebRequestWithSslRequired" xml:space="preserve">
+ <value>Insecure web request for '{0}' aborted due to security requirements demanding HTTPS.</value>
+ </data>
+ <data name="InsufficientMessageProtection" xml:space="preserve">
+ <value>The {0} message required protections {{{1}}} but the channel could only apply {{{2}}}.</value>
+ </data>
+ <data name="InvalidCustomBindingElementOrder" xml:space="preserve">
+ <value>The customized binding element ordering is invalid.</value>
+ </data>
+ <data name="InvalidMessageParts" xml:space="preserve">
+ <value>Some part(s) of the message have invalid values: {0}</value>
+ </data>
+ <data name="InvalidNonceReceived" xml:space="preserve">
+ <value>The incoming message had an invalid or missing nonce.</value>
+ </data>
+ <data name="KeyAlreadyExists" xml:space="preserve">
+ <value>An item with the same key has already been added.</value>
+ </data>
+ <data name="MessagePartEncoderWrongType" xml:space="preserve">
+ <value>The value for {0}.{1} on member {1} was expected to derive from {2} but was {3}.</value>
+ </data>
+ <data name="MessagePartReadFailure" xml:space="preserve">
+ <value>Error while reading message '{0}' parameter '{1}' with value '{2}'.</value>
+ </data>
+ <data name="MessagePartValueBase64DecodingFault" xml:space="preserve">
+ <value>Message parameter '{0}' with value '{1}' failed to base64 decode.</value>
+ </data>
+ <data name="MessagePartWriteFailure" xml:space="preserve">
+ <value>Error while preparing message '{0}' parameter '{1}' for sending.</value>
+ </data>
+ <data name="QueuedMessageResponseAlreadyExists" xml:space="preserve">
+ <value>A message response is already queued for sending in the response stream.</value>
+ </data>
+ <data name="ReplayAttackDetected" xml:space="preserve">
+ <value>This message has already been processed. This could indicate a replay attack in progress.</value>
+ </data>
+ <data name="ReplayProtectionNotSupported" xml:space="preserve">
+ <value>This channel does not support replay protection.</value>
+ </data>
+ <data name="RequiredNonEmptyParameterWasEmpty" xml:space="preserve">
+ <value>The following required non-empty parameters were empty in the {0} message: {1}</value>
+ </data>
+ <data name="RequiredParametersMissing" xml:space="preserve">
+ <value>The following required parameters were missing from the {0} message: {1}</value>
+ </data>
+ <data name="RequiredProtectionMissing" xml:space="preserve">
+ <value>The binding element offering the {0} protection requires other protection that is not provided.</value>
+ </data>
+ <data name="SequenceContainsNoElements" xml:space="preserve">
+ <value>The list is empty.</value>
+ </data>
+ <data name="SequenceContainsNullElement" xml:space="preserve">
+ <value>The list contains a null element.</value>
+ </data>
+ <data name="SignatureInvalid" xml:space="preserve">
+ <value>Message signature was incorrect.</value>
+ </data>
+ <data name="SigningNotSupported" xml:space="preserve">
+ <value>This channel does not support signing messages. To support signing messages, a derived Channel type must override the Sign and IsSignatureValid methods.</value>
+ </data>
+ <data name="StreamUnreadable" xml:space="preserve">
+ <value>The stream's CanRead property returned false.</value>
+ </data>
+ <data name="StreamUnwritable" xml:space="preserve">
+ <value>The stream's CanWrite property returned false.</value>
+ </data>
+ <data name="TooManyBindingsOfferingSameProtection" xml:space="preserve">
+ <value>Expected at most 1 binding element to apply the {0} protection, but more than one applied.</value>
+ </data>
+ <data name="TooManyRedirects" xml:space="preserve">
+ <value>The maximum allowable number of redirects were exceeded while requesting '{0}'.</value>
+ </data>
+ <data name="UnexpectedEmptyArray" xml:space="preserve">
+ <value>The array must not be empty.</value>
+ </data>
+ <data name="UnexpectedEmptyString" xml:space="preserve">
+ <value>The empty string is not allowed.</value>
+ </data>
+ <data name="UnexpectedMessagePartValue" xml:space="preserve">
+ <value>Message parameter '{0}' had unexpected value '{1}'.</value>
+ </data>
+ <data name="UnexpectedMessagePartValueForConstant" xml:space="preserve">
+ <value>Expected message {0} parameter '{1}' to have value '{2}' but had '{3}' instead.</value>
+ </data>
+ <data name="UnexpectedMessageReceived" xml:space="preserve">
+ <value>Expected message {0} but received {1} instead.</value>
+ </data>
+ <data name="UnexpectedMessageReceivedOfMany" xml:space="preserve">
+ <value>Unexpected message type received.</value>
+ </data>
+ <data name="UnexpectedNullValue" xml:space="preserve">
+ <value>A null value was included and is not allowed.</value>
+ </data>
+ <data name="UnexpectedType" xml:space="preserve">
+ <value>The type {0} or a derived type was expected, but {1} was given.</value>
+ </data>
+ <data name="UnrecognizedEnumValue" xml:space="preserve">
+ <value>{0} property has unrecognized value {1}.</value>
+ </data>
+ <data name="UnsafeWebRequestDetected" xml:space="preserve">
+ <value>The URL '{0}' is rated unsafe and cannot be requested this way.</value>
+ </data>
+ <data name="UntrustedRedirectsOnPOSTNotSupported" xml:space="preserve">
+ <value>Redirects on POST requests that are to untrusted servers is not supported.</value>
+ </data>
+ <data name="WebRequestFailed" xml:space="preserve">
+ <value>Web request to '{0}' failed.</value>
+ </data>
</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
index 96ec91b..4191f28 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
@@ -293,16 +293,16 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="args">The dictionary of key/values to read from.</param>
/// <returns>The formulated querystring style string.</returns>
- internal static string CreateQueryString(IDictionary<string, string> args) {
- if (args == null) {
- throw new ArgumentNullException("args");
- }
- if (args.Count == 0) {
+ internal static string CreateQueryString(IEnumerable<KeyValuePair<string, string>> args) {
+ ErrorUtilities.VerifyArgumentNotNull(args, "args");
+ if (args.Count() == 0) {
return string.Empty;
}
- StringBuilder sb = new StringBuilder(args.Count * 10);
+ StringBuilder sb = new StringBuilder(args.Count() * 10);
foreach (var p in args) {
+ ErrorUtilities.VerifyArgument(p.Key != null, MessagingStrings.UnexpectedNullValue);
+ ErrorUtilities.VerifyArgument(p.Value != null, MessagingStrings.UnexpectedNullValue);
sb.Append(HttpUtility.UrlEncode(p.Key));
sb.Append('=');
sb.Append(HttpUtility.UrlEncode(p.Value));
@@ -323,13 +323,13 @@ namespace DotNetOpenAuth.Messaging {
/// The arguments to add to the query.
/// If null, <paramref name="builder"/> is not changed.
/// </param>
- internal static void AppendQueryArgs(UriBuilder builder, IDictionary<string, string> args) {
+ internal static void AppendQueryArgs(this UriBuilder builder, IEnumerable<KeyValuePair<string, string>> args) {
if (builder == null) {
throw new ArgumentNullException("builder");
}
- if (args != null && args.Count > 0) {
- StringBuilder sb = new StringBuilder(50 + (args.Count * 10));
+ if (args != null && args.Count() > 0) {
+ StringBuilder sb = new StringBuilder(50 + (args.Count() * 10));
if (!string.IsNullOrEmpty(builder.Query)) {
sb.Append(builder.Query.Substring(1));
sb.Append('&');
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs
new file mode 100644
index 0000000..7480930
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs
@@ -0,0 +1,116 @@
+namespace DotNetOpenAuth.OpenId.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Messages;
+ using DotNetOpenAuth.Messaging.Reflection;
+
+ /// <summary>
+ /// Provides a mechanism for Relying Parties to work with OpenID 1.0 Providers
+ /// without losing claimed_id and op_endpoint data, which OpenID 2.0 Providers
+ /// are required to send back with positive assertions.
+ /// </summary>
+ internal class BackwardCompatibilityBindingElement : IChannelBindingElement {
+ private static readonly string ProviderEndpointParameterName = "dnoi.op_endpoint";
+
+ private static readonly string ClaimedIdentifierParameterName = "dnoi.claimed_id";
+
+ #region IChannelBindingElement Members
+
+ /// <summary>
+ /// Gets or sets the channel that this binding element belongs to.
+ /// </summary>
+ /// <value></value>
+ /// <remarks>
+ /// This property is set by the channel when it is first constructed.
+ /// </remarks>
+ public Channel Channel { get; set; }
+
+ /// <summary>
+ /// Gets the protection offered (if any) by this binding element.
+ /// </summary>
+ /// <value><see cref="MessageProtections.None"/></value>
+ public MessageProtections Protection {
+ get { return MessageProtections.None; }
+ }
+
+ /// <summary>
+ /// Prepares a message for sending based on the rules of this channel binding element.
+ /// </summary>
+ /// <param name="message">The message to prepare for sending.</param>
+ /// <returns>
+ /// True if the <paramref name="message"/> applied to this binding element
+ /// and the operation was successful. False otherwise.
+ /// </returns>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ public bool PrepareMessageForSending(IProtocolMessage message) {
+ SignedResponseRequest request = message as SignedResponseRequest;
+ if (request != null && request.Version.Major < 2) {
+ request.AddReturnToArguments(ProviderEndpointParameterName, request.Recipient.AbsoluteUri);
+
+ CheckIdRequest authRequest = request as CheckIdRequest;
+ if (authRequest != null) {
+ request.AddReturnToArguments(ClaimedIdentifierParameterName, authRequest.ClaimedIdentifier);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Performs any transformation on an incoming message that may be necessary and/or
+ /// validates an incoming message based on the rules of this channel binding element.
+ /// </summary>
+ /// <param name="message">The incoming message to process.</param>
+ /// <returns>
+ /// True if the <paramref name="message"/> applied to this binding element
+ /// and the operation was successful. False if the operation did not apply to this message.
+ /// </returns>
+ /// <exception cref="ProtocolException">
+ /// Thrown when the binding element rules indicate that this message is invalid and should
+ /// NOT be processed.
+ /// </exception>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ public bool PrepareMessageForReceiving(IProtocolMessage message) {
+ IndirectSignedResponse response = message as IndirectSignedResponse;
+ if (response != null && response.Version.Major < 2) {
+ var dictionary = new MessageDictionary(response);
+ var protocol = Protocol.Lookup(response.Version);
+
+ // Although GetReturnToArgument may return null if the parameters are not signed,
+ // the ReturnToSignatureBindingElement should have thrown an exception already
+ // if this is a 1.0 OP signed response without a valid signature since 1.0 OPs
+ // are not supposed to be able to send unsolicited assertions.
+ // Any safe solicited assertion would include our signature, allowing us to find
+ // these values.
+ if (response.ProviderEndpoint == null) {
+ string op_endpoint = response.GetReturnToArgument(ProviderEndpointParameterName);
+ response.ProviderEndpoint = new Uri(op_endpoint);
+ }
+
+ PositiveAssertionResponse authResponse = response as PositiveAssertionResponse;
+ if (authResponse != null) {
+ if (authResponse.ClaimedIdentifier == null) {
+ authResponse.ClaimedIdentifier = response.GetReturnToArgument(ClaimedIdentifierParameterName);
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/IPrivateSecretStore.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/IPrivateSecretStore.cs
new file mode 100644
index 0000000..1702212
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/IPrivateSecretStore.cs
@@ -0,0 +1,18 @@
+//-----------------------------------------------------------------------
+// <copyright file="IPrivateSecretStore.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.ChannelElements {
+ /// <summary>
+ /// Provides access to and persists a private secret that is used for signing.
+ /// </summary>
+ public interface IPrivateSecretStore {
+ /// <summary>
+ /// Gets or sets a secret key that can be used for signing.
+ /// </summary>
+ /// <value>A 64-byte binary value, which may contain null bytes.</value>
+ byte[] PrivateSecret { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs
index 1ed65cb..ff1b348 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs
@@ -42,8 +42,9 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// </summary>
/// <param name="associationStore">The association store to use.</param>
/// <param name="nonceStore">The nonce store to use.</param>
- internal OpenIdChannel(IAssociationStore<Uri> associationStore, INonceStore nonceStore)
- : this(associationStore, nonceStore, new OpenIdMessageFactory()) {
+ /// <param name="secretStore">The secret store to use.</param>
+ internal OpenIdChannel(IAssociationStore<Uri> associationStore, INonceStore nonceStore, IPrivateSecretStore secretStore)
+ : this(associationStore, nonceStore, secretStore, new OpenIdMessageFactory()) {
}
/// <summary>
@@ -62,9 +63,10 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// </summary>
/// <param name="associationStore">The association store to use.</param>
/// <param name="nonceStore">The nonce store to use.</param>
+ /// <param name="secretStore">The secret store to use.</param>
/// <param name="messageTypeProvider">An object that knows how to distinguish the various OpenID message types for deserialization purposes.</param>
- private OpenIdChannel(IAssociationStore<Uri> associationStore, INonceStore nonceStore, IMessageFactory messageTypeProvider) :
- base(messageTypeProvider, InitializeBindingElements(new SigningBindingElement(associationStore), nonceStore)) {
+ private OpenIdChannel(IAssociationStore<Uri> associationStore, INonceStore nonceStore, IPrivateSecretStore secretStore, IMessageFactory messageTypeProvider) :
+ this(messageTypeProvider, InitializeBindingElements(new SigningBindingElement(associationStore), nonceStore, secretStore, true)) {
}
/// <summary>
@@ -75,7 +77,26 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// <param name="nonceStore">The nonce store to use.</param>
/// <param name="messageTypeProvider">An object that knows how to distinguish the various OpenID message types for deserialization purposes.</param>
private OpenIdChannel(IAssociationStore<AssociationRelyingPartyType> associationStore, INonceStore nonceStore, IMessageFactory messageTypeProvider) :
- base(messageTypeProvider, InitializeBindingElements(new SigningBindingElement(associationStore), nonceStore)) {
+ this(messageTypeProvider, InitializeBindingElements(new SigningBindingElement(associationStore), nonceStore, null, false)) {
+ }
+
+ private OpenIdChannel(IMessageFactory messageTypeProvider, IChannelBindingElement[] bindingElements)
+ : base(messageTypeProvider, bindingElements) {
+ // Customize the binding element order, since we play some tricks for higher
+ // security and backward compatibility with older OpenID versions.
+ var outgoingBindingElements = new List<IChannelBindingElement>(bindingElements);
+ var incomingBindingElements = new List<IChannelBindingElement>(bindingElements);
+ incomingBindingElements.Reverse();
+
+ // Customize the order of the incoming elements by moving the return_to elements in front.
+ var backwardCompatibility = incomingBindingElements.OfType<BackwardCompatibilityBindingElement>().SingleOrDefault();
+ var returnToSign = incomingBindingElements.OfType<ReturnToSignatureBindingElement>().SingleOrDefault();
+ if (backwardCompatibility != null && returnToSign != null) {
+ incomingBindingElements.MoveTo(0, returnToSign);
+ incomingBindingElements.MoveTo(1, backwardCompatibility);
+ }
+
+ CustomizeBindingElementOrder(outgoingBindingElements, incomingBindingElements);
}
/// <summary>
@@ -165,15 +186,31 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// </summary>
/// <param name="signingElement">The signing element, previously constructed.</param>
/// <param name="nonceStore">The nonce store to use.</param>
- /// <returns>An array of binding elements which may be used to construct the channel.</returns>
- private static IChannelBindingElement[] InitializeBindingElements(SigningBindingElement signingElement, INonceStore nonceStore) {
+ /// <param name="secretStore">The secret store to use.</param>
+ /// <param name="isRelyingPartyRole">A value indicating whether this channel is being constructed for a Relying Party (as opposed to a Provider).</param>
+ /// <returns>
+ /// An array of binding elements which may be used to construct the channel.
+ /// </returns>
+ private static IChannelBindingElement[] InitializeBindingElements(SigningBindingElement signingElement, INonceStore nonceStore, IPrivateSecretStore secretStore, bool isRelyingPartyRole) {
ErrorUtilities.VerifyArgumentNotNull(signingElement, "signingElement");
- List<IChannelBindingElement> elements = new List<IChannelBindingElement>(3);
- elements.Add(signingElement);
+ List<IChannelBindingElement> elements = new List<IChannelBindingElement>(7);
+ elements.Add(new ExtensionsBindingElement(new OpenIdExtensionFactory()));
+ if (isRelyingPartyRole) {
+ ErrorUtilities.VerifyArgumentNotNull(secretStore, "secretStore");
+ secretStore.InitializeSecretIfUnset();
+
+ // It is important that the return_to signing element comes last
+ // so that the nonce is included in the signature.
+ elements.Add(new BackwardCompatibilityBindingElement());
+ elements.Add(new ReturnToNonceBindingElement(nonceStore));
+ elements.Add(new ReturnToSignatureBindingElement(secretStore));
+ }
+
elements.Add(new StandardReplayProtectionBindingElement(nonceStore, true));
elements.Add(new StandardExpirationBindingElement());
- elements.Add(new ExtensionsBindingElement(new OpenIdExtensionFactory()));
+ elements.Add(signingElement);
+
return elements.ToArray();
}
}
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs
new file mode 100644
index 0000000..d439818
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToNonceBindingElement.cs
@@ -0,0 +1,113 @@
+//-----------------------------------------------------------------------
+// <copyright file="ReturnToNonceBindingElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// This binding element adds a nonce to a Relying Party's outgoing
+ /// authentication request when working against an OpenID 1.0 Provider
+ /// in order to protect against replay attacks.
+ /// </summary>
+ /// <remarks>
+ /// <para>In the messaging stack, this binding element looks like an ordinary
+ /// transform-type of binding element rather than a protection element,
+ /// due to its required order in the channel stack and that it exists
+ /// only on the RP side and only on 1.0 messages.</para>
+ /// </remarks>
+ internal class ReturnToNonceBindingElement : IChannelBindingElement {
+ private static readonly string NonceParameter = "dnoi.request_nonce";
+ private static readonly int NonceByteLength = 128 / 8; // 128-bit nonce
+
+ private INonceStore nonceStore;
+
+ internal ReturnToNonceBindingElement(INonceStore nonceStore) {
+ ErrorUtilities.VerifyArgumentNotNull(nonceStore, "nonceStore");
+
+ this.nonceStore = nonceStore;
+ }
+
+ #region IChannelBindingElement Members
+
+ public Channel Channel { get; set; }
+
+ public MessageProtections Protection {
+ get { return MessageProtections.ReplayProtection; }
+ }
+
+ public bool PrepareMessageForSending(IProtocolMessage message) {
+ // We only add a nonce to 1.x auth requests.
+ SignedResponseRequest request = message as SignedResponseRequest;
+ if (request != null && request.Version.Major < 2) {
+ request.AddReturnToArguments(NonceParameter, CustomNonce.NewNonce().Serialize());
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public bool PrepareMessageForReceiving(IProtocolMessage message) {
+ IndirectSignedResponse response = message as IndirectSignedResponse;
+ if (response != null && response.Version.Major < 2) {
+ string nonceValue = response.GetReturnToArgument(NonceParameter);
+ ErrorUtilities.VerifyProtocol(nonceValue != null, OpenIdStrings.UnsolicitedAssertionsNotAllowedFrom1xOPs);
+
+ CustomNonce nonce = CustomNonce.Deserialize(nonceValue);
+ ErrorUtilities.VerifyProtocol(this.nonceStore.StoreNonce(nonce.RandomPartAsString, nonce.CreationDateUtc), MessagingStrings.ReplayAttackDetected);
+ return true;
+ }
+
+ return false;
+ }
+
+ #endregion
+
+ private class CustomNonce {
+ private byte[] randomPart;
+
+ private CustomNonce(DateTime creationDate, byte[] randomPart) {
+ this.CreationDateUtc = creationDate;
+ this.randomPart = randomPart;
+ }
+
+ internal static CustomNonce NewNonce() {
+ return new CustomNonce(DateTime.UtcNow, MessagingUtilities.GetCryptoRandomData(NonceByteLength));
+ }
+
+ internal DateTime CreationDateUtc { get; private set; }
+
+ internal string RandomPartAsString {
+ get { return Convert.ToBase64String(this.randomPart); }
+ }
+
+ internal string Serialize() {
+ byte[] timestamp = BitConverter.GetBytes(this.CreationDateUtc.Ticks);
+ byte[] nonce = new byte[timestamp.Length + this.randomPart.Length];
+ timestamp.CopyTo(nonce, 0);
+ this.randomPart.CopyTo(nonce, timestamp.Length);
+ string base64Nonce = Convert.ToBase64String(nonce);
+ return base64Nonce;
+ }
+
+ internal static CustomNonce Deserialize(string value) {
+ ErrorUtilities.VerifyNonZeroLength(value, "value");
+
+ byte[] nonce = Convert.FromBase64String(value);
+ DateTime creationDateUtc = new DateTime(BitConverter.ToInt64(nonce, 0), DateTimeKind.Utc);
+ byte[] randomPart = new byte[NonceByteLength];
+ Array.Copy(nonce, sizeof(long), randomPart, 0, NonceByteLength);
+ return new CustomNonce(creationDateUtc, randomPart);
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs
new file mode 100644
index 0000000..081f60d
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs
@@ -0,0 +1,179 @@
+//-----------------------------------------------------------------------
+// <copyright file="ReturnToSignatureBindingElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Messages;
+ using System.Web;
+ using System.Security.Cryptography;
+ using System.Collections.Specialized;
+
+ /// <summary>
+ /// This binding element signs a Relying Party's openid.return_to parameter
+ /// so that upon return, it can verify that it hasn't been tampered with.
+ /// </summary>
+ /// <remarks>
+ /// <para>Since Providers can send unsolicited assertions, not all openid.return_to
+ /// values will be signed. But those that are signed will be validated, and
+ /// any invalid or missing signatures will cause this library to not trust
+ /// the parameters in the return_to URL.</para>
+ /// <para>In the messaging stack, this binding element looks like an ordinary
+ /// transform-type of binding element rather than a protection element,
+ /// due to its required order in the channel stack and that it doesn't sign
+ /// anything except a particular message part.</para>
+ /// </remarks>
+ internal class ReturnToSignatureBindingElement : IChannelBindingElement {
+ /// <summary>
+ /// The optimal length for a private secret used for signing using the HMACSHA256 class.
+ /// </summary>
+ /// <remarks>
+ /// The 64-byte length is optimized for highest security when used with HMACSHA256.
+ /// See HMACSHA256.HMACSHA256(byte[]) documentation for more information.
+ /// </remarks>
+ internal static readonly int OptimalPrivateSecretLength = 64;
+
+ private static readonly string ReturnToSignatureParameterName = "dnoi.return_to_sig";
+
+ private HashAlgorithm signingHasher;
+
+ internal ReturnToSignatureBindingElement(IPrivateSecretStore secretStore) {
+ ErrorUtilities.VerifyArgumentNotNull(secretStore, "secretStore");
+ ErrorUtilities.VerifyInternal(secretStore.PrivateSecret != null, "Private secret should have been set already.");
+
+ if (secretStore.PrivateSecret.Length < OptimalPrivateSecretLength) {
+ Logger.WarnFormat("For best security, the optimal length of a private signing secret is {0} bytes, but the secret we have is only {1} bytes.", OptimalPrivateSecretLength, secretStore.PrivateSecret.Length);
+ }
+
+ this.signingHasher = new HMACSHA256(secretStore.PrivateSecret);
+ }
+
+ #region IChannelBindingElement Members
+
+ /// <summary>
+ /// Gets or sets the channel that this binding element belongs to.
+ /// </summary>
+ /// <value></value>
+ /// <remarks>
+ /// This property is set by the channel when it is first constructed.
+ /// </remarks>
+ public Channel Channel { get; set; }
+
+ /// <summary>
+ /// Gets the protection offered (if any) by this binding element.
+ /// </summary>
+ /// <value><see cref="MessageProtections.None"/></value>
+ public MessageProtections Protection {
+ get { return MessageProtections.None; }
+ }
+
+ /// <summary>
+ /// Prepares a message for sending based on the rules of this channel binding element.
+ /// </summary>
+ /// <param name="message">The message to prepare for sending.</param>
+ /// <returns>
+ /// True if the <paramref name="message"/> applied to this binding element
+ /// and the operation was successful. False otherwise.
+ /// </returns>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ public bool PrepareMessageForSending(IProtocolMessage message) {
+ SignedResponseRequest request = message as SignedResponseRequest;
+ if (request != null) {
+ string signature = GetReturnToSignature(request.ReturnTo);
+ request.AddReturnToArguments(ReturnToSignatureParameterName, signature);
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Performs any transformation on an incoming message that may be necessary and/or
+ /// validates an incoming message based on the rules of this channel binding element.
+ /// </summary>
+ /// <param name="message">The incoming message to process.</param>
+ /// <returns>
+ /// True if the <paramref name="message"/> applied to this binding element
+ /// and the operation was successful. False if the operation did not apply to this message.
+ /// </returns>
+ /// <exception cref="ProtocolException">
+ /// Thrown when the binding element rules indicate that this message is invalid and should
+ /// NOT be processed.
+ /// </exception>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ public bool PrepareMessageForReceiving(IProtocolMessage message) {
+ IndirectSignedResponse response = message as IndirectSignedResponse;
+
+ if (response != null) {
+ // This binding element tends to get "speedboated" to higher in the channel stack
+ // by the SigningBindingElement. Only do the work here if it hasn't been done yet
+ // to avoid signing twice (once for the SigningBindingElement and once for the channel).
+ if (!response.ReturnToParametersSignatureValidated) {
+ // We can't use response.GetReturnToArgument(string) because that relies
+ // on us already having validated this signature.
+ NameValueCollection returnToParameters = HttpUtility.ParseQueryString(response.ReturnTo.Query);
+
+ // Set the safety flag showing whether the return_to url had a valid signature.
+ string expected = GetReturnToSignature(response.ReturnTo);
+ string actual = returnToParameters[ReturnToSignatureParameterName];
+ response.ReturnToParametersSignatureValidated = actual == expected;
+ if (!response.ReturnToParametersSignatureValidated) {
+ Logger.WarnFormat("The return_to signature failed verification.");
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets the return to signature.
+ /// </summary>
+ /// <param name="returnTo">The return to.</param>
+ /// <returns></returns>
+ /// <remarks>
+ /// Only the parameters in the return_to URI are signed, rather than the base URI
+ /// itself, in order that OPs that might change the return_to's implicit port :80 part
+ /// or other minor changes do not invalidate the signature.
+ /// </remarks>
+ private string GetReturnToSignature(Uri returnTo) {
+ ErrorUtilities.VerifyArgumentNotNull(returnTo, "returnTo");
+
+ // Assemble the dictionary to sign, taking care to remove the signature itself
+ // in order to accurately reproduce the original signature (which of course didn't include
+ // the signature).
+ // Also we need to sort the dictionary's keys so that we sign in the same order as we did
+ // the last time.
+ var returnToParameters = HttpUtility.ParseQueryString(returnTo.Query).ToDictionary();
+ returnToParameters.Remove(ReturnToSignatureParameterName);
+ var sortedReturnToParameters = new SortedDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ foreach (var pair in returnToParameters) {
+ sortedReturnToParameters.Add(pair.Key, pair.Value);
+ }
+
+ Logger.DebugFormat("ReturnTo signed data: {0}", sortedReturnToParameters.ToStringDeferred());
+
+ // Sign the parameters.
+ byte[] bytesToSign = KeyValueFormEncoding.GetBytes(sortedReturnToParameters);
+ byte[] signature = this.signingHasher.ComputeHash(bytesToSign);
+ string signatureBase64 = Convert.ToBase64String(signature);
+ return signatureBase64;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs
index 40420da..ec438c9 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/IndirectSignedResponse.cs
@@ -42,6 +42,10 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// </summary>
private DateTime creationDateUtc = DateTime.UtcNow;
+ private IDictionary<string, string> returnToParameters;
+
+ private bool returnToParametersSignatureValidated;
+
/// <summary>
/// Initializes a new instance of the <see cref="IndirectSignedResponse"/> class.
/// </summary>
@@ -106,9 +110,23 @@ namespace DotNetOpenAuth.OpenId.Messages {
/// <summary>
/// Gets the level of protection this message requires.
/// </summary>
- /// <value><see cref="MessageProtections.All"/></value>
+ /// <value>
+ /// <see cref="MessageProtections.All"/> for OpenID 2.0 messages.
+ /// <see cref="MessageProtections.TamperProtection"/> for OpenID 1.x messages.
+ /// </value>
+ /// <remarks>
+ /// Although the required protection is reduced for OpenID 1.x,
+ /// this library will provide Relying Party hosts with all protections
+ /// by adding its own specially-crafted nonce to the authentication request
+ /// messages.
+ /// </remarks>
public override MessageProtections RequiredProtection {
- get { return MessageProtections.All; }
+ // TODO: Fix up Provider side to only use private associations (dumb mode),
+ // and then to add a special nonce value of its own to provide replay
+ // protection to the Provider's users even when logging into 1.0 RPs.
+ // When this is done, remove this conditional getter and always return
+ // that All protections are required.
+ get { return this.Version.Major < 2 ? MessageProtections.TamperProtection : MessageProtections.All; }
}
/// <summary>
@@ -182,6 +200,30 @@ namespace DotNetOpenAuth.OpenId.Messages {
internal Uri ReturnTo { get; set; }
/// <summary>
+ /// Gets or sets a value indicating whether the <see cref="ReturnTo"/>
+ /// URI's query string is unaltered between when the Relying Party
+ /// sent the original request and when the response was received.
+ /// </summary>
+ /// <remarks>
+ /// This property is not persisted in the transmitted message, and
+ /// has no effect on the Provider-side of the communication.
+ /// </remarks>
+ internal bool ReturnToParametersSignatureValidated {
+ get {
+ return this.returnToParametersSignatureValidated;
+ }
+
+ set {
+ if (this.returnToParametersSignatureValidated == value) {
+ return;
+ }
+
+ this.returnToParametersSignatureValidated = value;
+ this.returnToParameters = null;
+ }
+ }
+
+ /// <summary>
/// Gets or sets the nonce that will protect the message from replay attacks.
/// </summary>
/// <value>
@@ -218,6 +260,22 @@ namespace DotNetOpenAuth.OpenId.Messages {
}
}
+ private IDictionary<string, string> ReturnToParameters {
+ get {
+ if (this.returnToParameters == null) {
+ // Only return data that can be validated as untampered with.
+ if (this.ReturnToParametersSignatureValidated) {
+ this.returnToParameters = HttpUtility.ParseQueryString(this.ReturnTo.Query).ToDictionary();
+ } else {
+ // Store an empty dictionary since we can't consider any callback data reliable.
+ this.returnToParameters = new Dictionary<string, string>(0);
+ }
+ }
+
+ return this.returnToParameters;
+ }
+ }
+
/// <summary>
/// Checks the message state for conformity to the protocol specification
/// and throws an exception if the message is invalid.
@@ -265,5 +323,14 @@ namespace DotNetOpenAuth.OpenId.Messages {
this.ReturnTo,
this.Recipient);
}
+
+ internal string GetReturnToArgument(string key) {
+ ErrorUtilities.VerifyNonZeroLength(key, "key");
+ ErrorUtilities.VerifyInternal(ReturnTo != null, "ReturnTo was expected to be required but is null.");
+
+ string value;
+ this.ReturnToParameters.TryGetValue(key, out value);
+ return value;
+ }
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/SignedResponseRequest.cs b/src/DotNetOpenAuth/OpenId/Messages/SignedResponseRequest.cs
index aaf6004..f35741e 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/SignedResponseRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/SignedResponseRequest.cs
@@ -128,6 +128,18 @@ namespace DotNetOpenAuth.OpenId.Messages {
}
}
+ internal void AddReturnToArguments(IEnumerable<KeyValuePair<string, string>> keysValues) {
+ ErrorUtilities.VerifyArgumentNotNull(keysValues, "keysValues");
+ UriBuilder returnToBuilder = new UriBuilder(this.ReturnTo);
+ returnToBuilder.AppendQueryArgs(keysValues);
+ this.ReturnTo = returnToBuilder.Uri;
+ }
+
+ internal void AddReturnToArguments(string key, string value) {
+ var pair = new KeyValuePair<string, string>(key, value);
+ AddReturnToArguments(new[] { pair });
+ }
+
/// <summary>
/// Gets the value of the openid.mode parameter based on the protocol version and immediate flag.
/// </summary>
diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs
index 31eb749..7769455 100644
--- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs
+++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs
@@ -295,6 +295,15 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
+ /// Looks up a localized string similar to Unsolicited assertions are not allowed from 1.0 OpenID Providers..
+ /// </summary>
+ internal static string UnsolicitedAssertionsNotAllowedFrom1xOPs {
+ get {
+ return ResourceManager.GetString("UnsolicitedAssertionsNotAllowedFrom1xOPs", 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 8dc6ea9..e896fcf 100644
--- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx
+++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx
@@ -195,6 +195,9 @@
<data name="SregInvalidBirthdate" xml:space="preserve">
<value>Invalid birthdate value. Must be in the form yyyy-MM-dd.</value>
</data>
+ <data name="UnsolicitedAssertionsNotAllowedFrom1xOPs" xml:space="preserve">
+ <value>Unsolicited assertions are not allowed from 1.0 OpenID Providers.</value>
+ </data>
<data name="UserSetupUrlRequiredInImmediateNegativeResponse" xml:space="preserve">
<value>The openid.user_setup_url parameter is required when sending negative assertion messages in response to immediate mode requests.</value>
</data>
diff --git a/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs b/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs
index 82e6e32..21f7598 100644
--- a/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs
+++ b/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs
@@ -10,6 +10,7 @@ namespace DotNetOpenAuth.OpenId {
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.ChannelElements;
/// <summary>
/// A set of utilities especially useful to OpenID.
@@ -24,5 +25,24 @@ namespace DotNetOpenAuth.OpenId {
ErrorUtilities.VerifyArgumentNotNull(message, "message");
return Protocol.Lookup(message.Version);
}
+
+ internal static void MoveTo<T>(this IList<T> list, int position, T value) {
+ ErrorUtilities.VerifyInternal(list.Remove(value), "Unable to find element in list.");
+ list.Insert(position, value);
+ }
+
+ internal static void InitializeSecretIfUnset(this IPrivateSecretStore secretStore) {
+ ErrorUtilities.VerifyArgumentNotNull(secretStore, "secretStore");
+
+ if (secretStore.PrivateSecret == null) {
+ secretStore.PrivateSecret = MessagingUtilities.GetCryptoRandomData(ReturnToSignatureBindingElement.OptimalPrivateSecretLength);
+
+ // Log that we created a new private secret.
+ // If this happens frequently, it's a sign that the store for this secret is not
+ // properly saving the value, and the result will be slower performance for
+ // Relying Parties (at best) and failed authentications (at worst).
+ Logger.Info("Generated and saved private secret. This should generally happen only at web application initialization time.");
+ }
+ }
}
}
diff --git a/src/DotNetOpenAuth/OpenId/PrivateSecretMemoryStore.cs b/src/DotNetOpenAuth/OpenId/PrivateSecretMemoryStore.cs
new file mode 100644
index 0000000..77d9ba9
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/PrivateSecretMemoryStore.cs
@@ -0,0 +1,15 @@
+namespace DotNetOpenAuth.OpenId {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.OpenId.ChannelElements;
+
+ internal class PrivateSecretMemoryStore : IPrivateSecretStore {
+ #region IPrivateSecretStore Members
+
+ public byte[] PrivateSecret { get; set; }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
index 8bc0503..f4e03a5 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
@@ -1,108 +1,110 @@
-//-----------------------------------------------------------------------
-// <copyright file="OpenIdRelyingParty.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.RelyingParty {
- using System;
- using DotNetOpenAuth.Configuration;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Bindings;
- using DotNetOpenAuth.OpenId.ChannelElements;
- using DotNetOpenAuth.OpenId.Messages;
-
- /// <summary>
- /// Provides the programmatic facilities to act as an OpenId consumer.
- /// </summary>
- public sealed class OpenIdRelyingParty {
- /// <summary>
- /// The untrusted web request handler we use (and share) by default across all RP instances.
- /// </summary>
- private static IDirectSslWebRequestHandler defaultUntrustedWebRequestHandler = new UntrustedWebRequestHandler();
-
- /// <summary>
- /// Backing field for the <see cref="SecuritySettings"/> property.
- /// </summary>
- private RelyingPartySecuritySettings securitySettings;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="OpenIdRelyingParty"/> class.
- /// </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>
- public OpenIdRelyingParty(IAssociationStore<Uri> associationStore, INonceStore nonceStore) {
- // TODO: fix this so that a null association store is supported as 'dumb mode only'.
- ErrorUtilities.VerifyArgumentNotNull(associationStore, "associationStore");
- ErrorUtilities.VerifyArgumentNotNull(nonceStore, "nonceStore");
- ErrorUtilities.VerifyArgument((associationStore == null) == (nonceStore == null), OpenIdStrings.AssociationAndNonceStoresMustBeBothNullOrBothNonNull);
-
- this.Channel = new OpenIdChannel(associationStore, nonceStore);
- this.AssociationStore = associationStore;
- this.SecuritySettings = RelyingPartySection.Configuration.SecuritySettings.CreateSecuritySettings();
- this.WebRequestHandler = defaultUntrustedWebRequestHandler;
- }
-
- /// <summary>
- /// Gets the channel to use for sending/receiving messages.
- /// </summary>
- public Channel Channel { get; internal set; }
-
- /// <summary>
- /// Gets the security settings used by this Relying Party.
- /// </summary>
- public RelyingPartySecuritySettings SecuritySettings {
- get {
- return this.securitySettings;
- }
-
- internal set {
- if (value == null) {
- throw new ArgumentNullException("value");
- }
-
- this.securitySettings = value;
- }
- }
-
- /// <summary>
- /// Gets the association store.
- /// </summary>
- internal IAssociationStore<Uri> AssociationStore { get; private set; }
-
- /// <summary>
- /// Gets the web request handler to use for discovery and the part of
- /// authentication where direct messages are sent to an untrusted remote party.
- /// </summary>
- internal IDirectSslWebRequestHandler WebRequestHandler { get; private set; }
-
- /// <summary>
- /// Gets an association between this Relying Party and a given Provider.
- /// A new association is created if necessary and possible.
- /// </summary>
- /// <param name="provider">The provider to create an association with.</param>
- /// <returns>The association if one exists and/or could be created. Null otherwise.</returns>
- internal Association GetAssociation(ProviderEndpointDescription provider) {
- ErrorUtilities.VerifyArgumentNotNull(provider, "provider");
-
- var associateRequest = AssociateRequest.Create(this.SecuritySettings, provider);
- if (associateRequest == null) {
- return null;
- }
-
- var associateResponse = this.Channel.Request(associateRequest);
- var associateSuccessfulResponse = associateResponse as AssociateSuccessfulResponse;
- var associateUnsuccessfulResponse = associateResponse as AssociateUnsuccessfulResponse;
- if (associateSuccessfulResponse != null) {
- Association association = associateSuccessfulResponse.CreateAssociation(associateRequest);
- this.AssociationStore.StoreAssociation(provider.Endpoint, association);
- return association;
- } else if (associateUnsuccessfulResponse != null) {
- // TODO: code here
- throw new NotImplementedException();
- } else {
- throw new ProtocolException(MessagingStrings.UnexpectedMessageReceivedOfMany);
- }
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="OpenIdRelyingParty.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.RelyingParty {
+ using System;
+ using DotNetOpenAuth.Configuration;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.OpenId.ChannelElements;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// Provides the programmatic facilities to act as an OpenId consumer.
+ /// </summary>
+ public sealed class OpenIdRelyingParty {
+ /// <summary>
+ /// The untrusted web request handler we use (and share) by default across all RP instances.
+ /// </summary>
+ private static IDirectSslWebRequestHandler defaultUntrustedWebRequestHandler = new UntrustedWebRequestHandler();
+
+ /// <summary>
+ /// Backing field for the <see cref="SecuritySettings"/> property.
+ /// </summary>
+ private RelyingPartySecuritySettings securitySettings;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OpenIdRelyingParty"/> class.
+ /// </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>
+ /// <param name="secretStore">The secret store to use. If null, the relying party will always operate in "dumb mode".</param>
+ public OpenIdRelyingParty(IAssociationStore<Uri> associationStore, INonceStore nonceStore, IPrivateSecretStore secretStore) {
+ // TODO: fix this so that a null association store is supported as 'dumb mode only'.
+ ErrorUtilities.VerifyArgumentNotNull(associationStore, "associationStore");
+ ErrorUtilities.VerifyArgumentNotNull(nonceStore, "nonceStore");
+ ErrorUtilities.VerifyArgumentNotNull(secretStore, "secretStore");
+ ErrorUtilities.VerifyArgument((associationStore == null) == (nonceStore == null), OpenIdStrings.AssociationAndNonceStoresMustBeBothNullOrBothNonNull);
+
+ this.Channel = new OpenIdChannel(associationStore, nonceStore, secretStore);
+ this.AssociationStore = associationStore;
+ this.SecuritySettings = RelyingPartySection.Configuration.SecuritySettings.CreateSecuritySettings();
+ this.WebRequestHandler = defaultUntrustedWebRequestHandler;
+ }
+
+ /// <summary>
+ /// Gets the channel to use for sending/receiving messages.
+ /// </summary>
+ public Channel Channel { get; internal set; }
+
+ /// <summary>
+ /// Gets the security settings used by this Relying Party.
+ /// </summary>
+ public RelyingPartySecuritySettings SecuritySettings {
+ get {
+ return this.securitySettings;
+ }
+
+ internal set {
+ if (value == null) {
+ throw new ArgumentNullException("value");
+ }
+
+ this.securitySettings = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets the association store.
+ /// </summary>
+ internal IAssociationStore<Uri> AssociationStore { get; private set; }
+
+ /// <summary>
+ /// Gets the web request handler to use for discovery and the part of
+ /// authentication where direct messages are sent to an untrusted remote party.
+ /// </summary>
+ internal IDirectSslWebRequestHandler WebRequestHandler { get; private set; }
+
+ /// <summary>
+ /// Gets an association between this Relying Party and a given Provider.
+ /// A new association is created if necessary and possible.
+ /// </summary>
+ /// <param name="provider">The provider to create an association with.</param>
+ /// <returns>The association if one exists and/or could be created. Null otherwise.</returns>
+ internal Association GetAssociation(ProviderEndpointDescription provider) {
+ ErrorUtilities.VerifyArgumentNotNull(provider, "provider");
+
+ var associateRequest = AssociateRequest.Create(this.SecuritySettings, provider);
+ if (associateRequest == null) {
+ return null;
+ }
+
+ var associateResponse = this.Channel.Request(associateRequest);
+ var associateSuccessfulResponse = associateResponse as AssociateSuccessfulResponse;
+ var associateUnsuccessfulResponse = associateResponse as AssociateUnsuccessfulResponse;
+ if (associateSuccessfulResponse != null) {
+ Association association = associateSuccessfulResponse.CreateAssociation(associateRequest);
+ this.AssociationStore.StoreAssociation(provider.Endpoint, association);
+ return association;
+ } else if (associateUnsuccessfulResponse != null) {
+ // TODO: code here
+ throw new NotImplementedException();
+ } else {
+ throw new ProtocolException(MessagingStrings.UnexpectedMessageReceivedOfMany);
+ }
+ }
+ }
+}