diff options
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'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'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 '{0}' 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 '{0}' parameter '{1}' with value '{2}'.. - /// </summary> - internal static string MessagePartReadFailure { - get { - return ResourceManager.GetString("MessagePartReadFailure", resourceCulture); - } - } - - /// <summary> - /// Looks up a localized string similar to Message parameter '{0}' with value '{1}' 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 '{0}' parameter '{1}' 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'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'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 '{0}'.. - /// </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 '{0}' had unexpected value '{1}'.. - /// </summary> - internal static string UnexpectedMessagePartValue { - get { - return ResourceManager.GetString("UnexpectedMessagePartValue", resourceCulture); - } - } - - /// <summary> - /// Looks up a localized string similar to Expected message {0} parameter '{1}' to have value '{2}' but had '{3}' 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 '{0}' 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 '{0}' 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'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'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 '{0}' 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 '{0}' parameter '{1}' with value '{2}'..
+ /// </summary>
+ internal static string MessagePartReadFailure {
+ get {
+ return ResourceManager.GetString("MessagePartReadFailure", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Message parameter '{0}' with value '{1}' 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 '{0}' parameter '{1}' 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'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'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 '{0}'..
+ /// </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 '{0}' had unexpected value '{1}'..
+ /// </summary>
+ internal static string UnexpectedMessagePartValue {
+ get {
+ return ResourceManager.GetString("UnexpectedMessagePartValue", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Expected message {0} parameter '{1}' to have value '{2}' but had '{3}' 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 '{0}' 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 '{0}' 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);
+ }
+ }
+ }
+}
|