summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/DotNetOAuth.Test/DotNetOAuth.Test.csproj2
-rw-r--r--src/DotNetOAuth.Test/Messaging/Bindings/StandardExpirationBindingElementTests.cs (renamed from src/DotNetOAuth.Test/Messaging/StandardExpirationBindingElementTests.cs)2
-rw-r--r--src/DotNetOAuth.Test/Messaging/ChannelTests.cs13
-rw-r--r--src/DotNetOAuth.Test/Messaging/MessagingTestBase.cs13
-rw-r--r--src/DotNetOAuth.Test/Mocks/MockReplayProtectionBindingElement.cs10
-rw-r--r--src/DotNetOAuth.Test/Mocks/MockSigningBindingElement.cs10
-rw-r--r--src/DotNetOAuth.Test/Mocks/MockTransformationBindingElement.cs10
-rw-r--r--src/DotNetOAuth.Test/Mocks/TestDirectedMessage.cs6
-rw-r--r--src/DotNetOAuth.Test/Mocks/TestSignedDirectedMessage.cs4
-rw-r--r--src/DotNetOAuth/DotNetOAuth.csproj1
-rw-r--r--src/DotNetOAuth/Messaging/Bindings/StandardExpirationBindingElement.cs11
-rw-r--r--src/DotNetOAuth/Messaging/Channel.cs35
-rw-r--r--src/DotNetOAuth/Messaging/IChannelBindingElement.cs16
-rw-r--r--src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs9
-rw-r--r--src/DotNetOAuth/Messaging/MessagingStrings.resx3
-rw-r--r--src/DotNetOAuth/Messaging/UnprotectedMessageException.cs30
16 files changed, 155 insertions, 20 deletions
diff --git a/src/DotNetOAuth.Test/DotNetOAuth.Test.csproj b/src/DotNetOAuth.Test/DotNetOAuth.Test.csproj
index 7d6c942..09b5974 100644
--- a/src/DotNetOAuth.Test/DotNetOAuth.Test.csproj
+++ b/src/DotNetOAuth.Test/DotNetOAuth.Test.csproj
@@ -64,7 +64,7 @@
<Compile Include="Messaging\DictionaryXmlReaderTests.cs" />
<Compile Include="Messaging\HttpRequestInfoTests.cs" />
<Compile Include="Messaging\ProtocolExceptionTests.cs" />
- <Compile Include="Messaging\StandardExpirationBindingElementTests.cs" />
+ <Compile Include="Messaging\Bindings\StandardExpirationBindingElementTests.cs" />
<Compile Include="Mocks\MockTransformationBindingElement.cs" />
<Compile Include="Mocks\MockReplayProtectionBindingElement.cs" />
<Compile Include="Mocks\TestBaseMessage.cs" />
diff --git a/src/DotNetOAuth.Test/Messaging/StandardExpirationBindingElementTests.cs b/src/DotNetOAuth.Test/Messaging/Bindings/StandardExpirationBindingElementTests.cs
index 84b12c6..6ba1cfc 100644
--- a/src/DotNetOAuth.Test/Messaging/StandardExpirationBindingElementTests.cs
+++ b/src/DotNetOAuth.Test/Messaging/Bindings/StandardExpirationBindingElementTests.cs
@@ -4,7 +4,7 @@
// </copyright>
//-----------------------------------------------------------------------
-namespace DotNetOAuth.Test.Messaging {
+namespace DotNetOAuth.Test.Messaging.Bindings {
using System;
using DotNetOAuth.Messaging;
using DotNetOAuth.Messaging.Bindings;
diff --git a/src/DotNetOAuth.Test/Messaging/ChannelTests.cs b/src/DotNetOAuth.Test/Messaging/ChannelTests.cs
index e4381a6..ac7e8d5 100644
--- a/src/DotNetOAuth.Test/Messaging/ChannelTests.cs
+++ b/src/DotNetOAuth.Test/Messaging/ChannelTests.cs
@@ -285,5 +285,18 @@ namespace DotNetOAuth.Test.Messaging {
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(MessageProtection.None, MessageProtection.TamperProtection);
+ this.ParameterizedReceiveProtectedTest(DateTime.Now, false);
+ }
}
}
diff --git a/src/DotNetOAuth.Test/Messaging/MessagingTestBase.cs b/src/DotNetOAuth.Test/Messaging/MessagingTestBase.cs
index b0b4eba..a21b000 100644
--- a/src/DotNetOAuth.Test/Messaging/MessagingTestBase.cs
+++ b/src/DotNetOAuth.Test/Messaging/MessagingTestBase.cs
@@ -59,18 +59,25 @@ namespace DotNetOAuth.Test {
}
internal static Channel CreateChannel(MessageProtection capability, MessageProtection recognition) {
- bool signing = false, expiration = false, replay = false;
var bindingElements = new List<IChannelBindingElement>();
if (capability >= MessageProtection.TamperProtection) {
bindingElements.Add(new MockSigningBindingElement());
- signing = true;
}
if (capability >= MessageProtection.Expiration) {
bindingElements.Add(new StandardExpirationBindingElement());
- expiration = true;
}
if (capability >= MessageProtection.ReplayProtection) {
bindingElements.Add(new MockReplayProtectionBindingElement());
+ }
+
+ bool signing = false, expiration = false, replay = false;
+ if (recognition >= MessageProtection.TamperProtection) {
+ signing = true;
+ }
+ if (recognition >= MessageProtection.Expiration) {
+ expiration = true;
+ }
+ if (recognition >= MessageProtection.ReplayProtection) {
replay = true;
}
diff --git a/src/DotNetOAuth.Test/Mocks/MockReplayProtectionBindingElement.cs b/src/DotNetOAuth.Test/Mocks/MockReplayProtectionBindingElement.cs
index 1b80b5b..805ace5 100644
--- a/src/DotNetOAuth.Test/Mocks/MockReplayProtectionBindingElement.cs
+++ b/src/DotNetOAuth.Test/Mocks/MockReplayProtectionBindingElement.cs
@@ -18,14 +18,17 @@ namespace DotNetOAuth.Test.Mocks {
get { return MessageProtection.ReplayProtection; }
}
- void IChannelBindingElement.PrepareMessageForSending(IProtocolMessage message) {
+ bool IChannelBindingElement.PrepareMessageForSending(IProtocolMessage message) {
var replayMessage = message as IReplayProtectedProtocolMessage;
if (replayMessage != null) {
replayMessage.Nonce = "someNonce";
+ return true;
}
+
+ return false;
}
- void IChannelBindingElement.PrepareMessageForReceiving(IProtocolMessage message) {
+ bool IChannelBindingElement.PrepareMessageForReceiving(IProtocolMessage message) {
var replayMessage = message as IReplayProtectedProtocolMessage;
if (replayMessage != null) {
Assert.AreEqual("someNonce", replayMessage.Nonce, "The nonce didn't serialize correctly, or something");
@@ -34,7 +37,10 @@ namespace DotNetOAuth.Test.Mocks {
throw new ReplayedMessageException(message);
}
this.messageReceived = true;
+ return true;
}
+
+ return false;
}
#endregion
diff --git a/src/DotNetOAuth.Test/Mocks/MockSigningBindingElement.cs b/src/DotNetOAuth.Test/Mocks/MockSigningBindingElement.cs
index 8aa8020..3dc9039 100644
--- a/src/DotNetOAuth.Test/Mocks/MockSigningBindingElement.cs
+++ b/src/DotNetOAuth.Test/Mocks/MockSigningBindingElement.cs
@@ -21,20 +21,26 @@ namespace DotNetOAuth.Test.Mocks {
get { return MessageProtection.TamperProtection; }
}
- void IChannelBindingElement.PrepareMessageForSending(IProtocolMessage message) {
+ bool IChannelBindingElement.PrepareMessageForSending(IProtocolMessage message) {
ISignedOAuthMessage signedMessage = message as ISignedOAuthMessage;
if (signedMessage != null) {
signedMessage.Signature = MessageSignature;
+ return true;
}
+
+ return false;
}
- void IChannelBindingElement.PrepareMessageForReceiving(IProtocolMessage message) {
+ bool IChannelBindingElement.PrepareMessageForReceiving(IProtocolMessage message) {
ISignedOAuthMessage signedMessage = message as ISignedOAuthMessage;
if (signedMessage != null) {
if (signedMessage.Signature != MessageSignature) {
throw new InvalidSignatureException(message);
}
+ return true;
}
+
+ return false;
}
#endregion
diff --git a/src/DotNetOAuth.Test/Mocks/MockTransformationBindingElement.cs b/src/DotNetOAuth.Test/Mocks/MockTransformationBindingElement.cs
index cd75e34..a569754 100644
--- a/src/DotNetOAuth.Test/Mocks/MockTransformationBindingElement.cs
+++ b/src/DotNetOAuth.Test/Mocks/MockTransformationBindingElement.cs
@@ -29,19 +29,25 @@ namespace DotNetOAuth.Test.Mocks {
get { return MessageProtection.None; }
}
- void IChannelBindingElement.PrepareMessageForSending(IProtocolMessage message) {
+ bool IChannelBindingElement.PrepareMessageForSending(IProtocolMessage message) {
var testMessage = message as TestMessage;
if (testMessage != null) {
testMessage.Name = this.transform + testMessage.Name;
+ return true;
}
+
+ return false;
}
- void IChannelBindingElement.PrepareMessageForReceiving(IProtocolMessage message) {
+ bool IChannelBindingElement.PrepareMessageForReceiving(IProtocolMessage message) {
var testMessage = message as TestMessage;
if (testMessage != null) {
StringAssert.StartsWith(testMessage.Name, this.transform);
testMessage.Name = testMessage.Name.Substring(this.transform.Length);
+ return true;
}
+
+ return false;
}
#endregion
diff --git a/src/DotNetOAuth.Test/Mocks/TestDirectedMessage.cs b/src/DotNetOAuth.Test/Mocks/TestDirectedMessage.cs
index 9e4cd38..30a290d 100644
--- a/src/DotNetOAuth.Test/Mocks/TestDirectedMessage.cs
+++ b/src/DotNetOAuth.Test/Mocks/TestDirectedMessage.cs
@@ -39,7 +39,7 @@ namespace DotNetOAuth.Test.Mocks {
}
MessageProtection IProtocolMessage.RequiredProtection {
- get { return MessageProtection.None; }
+ get { return this.RequiredProtection; }
}
MessageTransport IProtocolMessage.Transport {
@@ -53,5 +53,9 @@ namespace DotNetOAuth.Test.Mocks {
}
#endregion
+
+ protected virtual MessageProtection RequiredProtection {
+ get { return MessageProtection.None; }
+ }
}
}
diff --git a/src/DotNetOAuth.Test/Mocks/TestSignedDirectedMessage.cs b/src/DotNetOAuth.Test/Mocks/TestSignedDirectedMessage.cs
index 81cae4f..f051173 100644
--- a/src/DotNetOAuth.Test/Mocks/TestSignedDirectedMessage.cs
+++ b/src/DotNetOAuth.Test/Mocks/TestSignedDirectedMessage.cs
@@ -24,5 +24,9 @@ namespace DotNetOAuth.Test.Mocks {
}
#endregion
+
+ protected override MessageProtection RequiredProtection {
+ get { return MessageProtection.TamperProtection; }
+ }
}
}
diff --git a/src/DotNetOAuth/DotNetOAuth.csproj b/src/DotNetOAuth/DotNetOAuth.csproj
index 69b1186..ec83e9d 100644
--- a/src/DotNetOAuth/DotNetOAuth.csproj
+++ b/src/DotNetOAuth/DotNetOAuth.csproj
@@ -90,6 +90,7 @@
</Compile>
<Compile Include="Messaging\MessagingUtilities.cs" />
<Compile Include="Messaging\Bindings\StandardExpirationBindingElement.cs" />
+ <Compile Include="Messaging\UnprotectedMessageException.cs" />
<Compile Include="OAuthChannel.cs" />
<Compile Include="Messaging\Response.cs" />
<Compile Include="Messaging\IProtocolMessage.cs" />
diff --git a/src/DotNetOAuth/Messaging/Bindings/StandardExpirationBindingElement.cs b/src/DotNetOAuth/Messaging/Bindings/StandardExpirationBindingElement.cs
index 154ea13..ef93e0d 100644
--- a/src/DotNetOAuth/Messaging/Bindings/StandardExpirationBindingElement.cs
+++ b/src/DotNetOAuth/Messaging/Bindings/StandardExpirationBindingElement.cs
@@ -71,11 +71,14 @@ namespace DotNetOAuth.Messaging.Bindings {
/// Sets the timestamp on an outgoing message.
/// </summary>
/// <param name="message">The outgoing message.</param>
- void IChannelBindingElement.PrepareMessageForSending(IProtocolMessage message) {
+ bool IChannelBindingElement.PrepareMessageForSending(IProtocolMessage message) {
IExpiringProtocolMessage expiringMessage = message as IExpiringProtocolMessage;
if (expiringMessage != null) {
expiringMessage.UtcCreationDate = DateTime.UtcNow;
+ return true;
}
+
+ return false;
}
/// <summary>
@@ -83,7 +86,7 @@ namespace DotNetOAuth.Messaging.Bindings {
/// </summary>
/// <param name="message">The incoming message.</param>
/// <exception cref="ExpiredMessageException">Thrown if the given message has already expired.</exception>
- void IChannelBindingElement.PrepareMessageForReceiving(IProtocolMessage message) {
+ bool IChannelBindingElement.PrepareMessageForReceiving(IProtocolMessage message) {
IExpiringProtocolMessage expiringMessage = message as IExpiringProtocolMessage;
if (expiringMessage != null) {
// Yes the UtcCreationDate is supposed to always be in UTC already,
@@ -92,7 +95,11 @@ namespace DotNetOAuth.Messaging.Bindings {
if (expirationDate < DateTime.UtcNow) {
throw new ExpiredMessageException(expirationDate, expiringMessage);
}
+
+ return true;
}
+
+ return false;
}
#endregion
diff --git a/src/DotNetOAuth/Messaging/Channel.cs b/src/DotNetOAuth/Messaging/Channel.cs
index 3c91b25..aebf6e4 100644
--- a/src/DotNetOAuth/Messaging/Channel.cs
+++ b/src/DotNetOAuth/Messaging/Channel.cs
@@ -189,9 +189,16 @@ namespace DotNetOAuth.Messaging {
/// <param name="request">The message to send.</param>
/// <returns>The remote party's response.</returns>
protected internal IProtocolMessage Request(IDirectedProtocolMessage request) {
+ if (request == null) {
+ throw new ArgumentNullException("request");
+ }
+
this.PrepareMessageForSending(request);
IProtocolMessage response = this.RequestInternal(request);
- this.VerifyMessageAfterReceiving(response);
+ if (response != null) {
+ this.VerifyMessageAfterReceiving(response);
+ }
+
return response;
}
@@ -466,7 +473,7 @@ namespace DotNetOAuth.Messaging {
/// 0 if it doesn't matter.
/// </returns>
private static int BindingElementOutgoingMessageApplicationOrder(MessageProtection protection1, MessageProtection protection2) {
- Debug.Assert(protection1 != MessageProtection.None && protection2 != MessageProtection.None, "This comparison function should only be used to compare protection binding elements. Otherwise we change the order of user-defined message transformations.");
+ Debug.Assert(protection1 != MessageProtection.None || protection2 != MessageProtection.None, "This comparison function should only be used to compare protection binding elements. Otherwise we change the order of user-defined message transformations.");
// Now put the protection ones in the right order.
return -((int)protection1).CompareTo((int)protection2); // descending flag ordinal order
@@ -477,8 +484,18 @@ namespace DotNetOAuth.Messaging {
/// </summary>
/// <param name="message">The message to prepare for sending.</param>
private void PrepareMessageForSending(IProtocolMessage message) {
+ Debug.Assert(message != null, "message == null");
+
+ MessageProtection appliedProtection = MessageProtection.None;
foreach (IChannelBindingElement bindingElement in this.bindingElements) {
- bindingElement.PrepareMessageForSending(message);
+ if (bindingElement.PrepareMessageForSending(message)) {
+ appliedProtection |= bindingElement.Protection;
+ }
+ }
+
+ // Ensure that the message's protection requirements have been satisfied.
+ if ((message.RequiredProtection & appliedProtection) != message.RequiredProtection) {
+ throw new UnprotectedMessageException(message, appliedProtection);
}
}
@@ -491,8 +508,18 @@ namespace DotNetOAuth.Messaging {
/// This can be due to tampering, replay attack or expiration, among other things.
/// </exception>
private void VerifyMessageAfterReceiving(IProtocolMessage message) {
+ Debug.Assert(message != null, "message == null");
+
+ MessageProtection appliedProtection = MessageProtection.None;
foreach (IChannelBindingElement bindingElement in this.bindingElements.Reverse<IChannelBindingElement>()) {
- bindingElement.PrepareMessageForReceiving(message);
+ if (bindingElement.PrepareMessageForReceiving(message)) {
+ appliedProtection |= bindingElement.Protection;
+ }
+ }
+
+ // Ensure that the message's protection requirements have been satisfied.
+ if ((message.RequiredProtection & appliedProtection) != message.RequiredProtection) {
+ throw new UnprotectedMessageException(message, appliedProtection);
}
}
}
diff --git a/src/DotNetOAuth/Messaging/IChannelBindingElement.cs b/src/DotNetOAuth/Messaging/IChannelBindingElement.cs
index b9f41f8..71127f4 100644
--- a/src/DotNetOAuth/Messaging/IChannelBindingElement.cs
+++ b/src/DotNetOAuth/Messaging/IChannelBindingElement.cs
@@ -24,13 +24,25 @@ namespace DotNetOAuth.Messaging {
/// 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>
- void PrepareMessageForSending(IProtocolMessage message);
+ /// <returns>
+ /// True if the <paramref name="message"/> applied to this binding element
+ /// and the operation was successful. False otherwise.
+ /// </returns>
+ bool PrepareMessageForSending(IProtocolMessage message);
/// <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>
- void PrepareMessageForReceiving(IProtocolMessage message);
+ /// <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>
+ bool PrepareMessageForReceiving(IProtocolMessage message);
}
}
diff --git a/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs b/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs
index 702a5be..49e726d 100644
--- a/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs
+++ b/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs
@@ -142,6 +142,15 @@ namespace DotNetOAuth.Messaging {
}
/// <summary>
+ /// Looks up a localized string similar to The message required protections {0} but the channel could only apply {1}..
+ /// </summary>
+ internal static string InsufficentMessageProtection {
+ get {
+ return ResourceManager.GetString("InsufficentMessageProtection", 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 {
diff --git a/src/DotNetOAuth/Messaging/MessagingStrings.resx b/src/DotNetOAuth/Messaging/MessagingStrings.resx
index a7dd42f..2133c44 100644
--- a/src/DotNetOAuth/Messaging/MessagingStrings.resx
+++ b/src/DotNetOAuth/Messaging/MessagingStrings.resx
@@ -144,6 +144,9 @@
<data name="IndirectMessagesMustImplementIDirectedProtocolMessage" xml:space="preserve">
<value>Messages that indicate indirect transport must implement the {0} interface.</value>
</data>
+ <data name="InsufficentMessageProtection" xml:space="preserve">
+ <value>The message required protections {0} but the channel could only apply {1}.</value>
+ </data>
<data name="QueuedMessageResponseAlreadyExists" xml:space="preserve">
<value>A message response is already queued for sending in the response stream.</value>
</data>
diff --git a/src/DotNetOAuth/Messaging/UnprotectedMessageException.cs b/src/DotNetOAuth/Messaging/UnprotectedMessageException.cs
new file mode 100644
index 0000000..f05bf88
--- /dev/null
+++ b/src/DotNetOAuth/Messaging/UnprotectedMessageException.cs
@@ -0,0 +1,30 @@
+namespace DotNetOAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using System.Globalization;
+
+ internal class UnprotectedMessageException : ProtocolException {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UnprotectedMessageException"/> class.
+ /// </summary>
+ /// <param name="faultedMessage">The message whose protection requirements could not be met.</param>
+ /// <param name="appliedProtection">The protection requirements that were fulfilled.</param>
+ internal UnprotectedMessageException(IProtocolMessage faultedMessage, MessageProtection appliedProtection)
+ : base(string.Format(CultureInfo.CurrentCulture, MessagingStrings.InsufficentMessageProtection, faultedMessage.RequiredProtection, appliedProtection), faultedMessage) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UnprotectedMessageException"/> 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 UnprotectedMessageException(
+ System.Runtime.Serialization.SerializationInfo info,
+ System.Runtime.Serialization.StreamingContext context)
+ : base(info, context) { }
+ }
+}