summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2008-12-19 16:24:25 -0800
committerAndrew <andrewarnott@gmail.com>2008-12-19 16:24:25 -0800
commit89c77e698e1e03f157db71da66576ef564c918ae (patch)
treeaa0e5c871b210d8a792d1847cb76ae283e772aab /src
parent77f5b1550164c409ee6b4e41c372037832d112c3 (diff)
downloadDotNetOpenAuth-89c77e698e1e03f157db71da66576ef564c918ae.zip
DotNetOpenAuth-89c77e698e1e03f157db71da66576ef564c918ae.tar.gz
DotNetOpenAuth-89c77e698e1e03f157db71da66576ef564c918ae.tar.bz2
Lots of work toward extensions.
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/ProtocolExceptionTests.cs194
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/TestBaseMessage.cs130
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/TestMessage.cs164
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs376
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Messages/IndirectSignedResponseTests.cs222
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Messages/PositiveAssertionResponseTests.cs146
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj588
-rw-r--r--src/DotNetOpenAuth/Messaging/Channel.cs1564
-rw-r--r--src/DotNetOpenAuth/Messaging/IExtensionMessage.cs13
-rw-r--r--src/DotNetOpenAuth/Messaging/IMessage.cs53
-rw-r--r--src/DotNetOpenAuth/Messaging/IProtocolMessage.cs90
-rw-r--r--src/DotNetOpenAuth/Messaging/IProtocolMessageWithExtensions.cs25
-rw-r--r--src/DotNetOpenAuth/Messaging/MessageSerializer.cs200
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingUtilities.cs944
-rw-r--r--src/DotNetOpenAuth/Messaging/ProtocolException.cs578
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs642
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs656
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs2
-rw-r--r--src/DotNetOpenAuth/OAuth/ConsumerBase.cs332
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs558
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/OAuth Messages.cd428
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs338
-rw-r--r--src/DotNetOpenAuth/OpenId/AliasManager.cs138
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs112
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs548
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/AliasManager.cs150
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/ExtensionArgumentsManager.cs149
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/ExtensionBase.cs124
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/IExtension.cs60
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/IIncomingExtensions.cs30
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/IOutgoingExtensions.cs20
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs245
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs226
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Constants.cs31
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/DemandLevel.cs31
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Gender.cs69
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs302
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs150
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/DirectResponseBase.cs310
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/IOpenIdProtocolMessageExtension.cs40
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/IndirectResponseBase.cs154
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/NegativeAssertionResponse.cs208
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs386
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs576
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdStrings.resx388
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs28
-rw-r--r--src/DotNetOpenAuth/Util.cs394
47 files changed, 7351 insertions, 5761 deletions
diff --git a/src/DotNetOpenAuth.Test/Messaging/ProtocolExceptionTests.cs b/src/DotNetOpenAuth.Test/Messaging/ProtocolExceptionTests.cs
index 02aea64..f245702 100644
--- a/src/DotNetOpenAuth.Test/Messaging/ProtocolExceptionTests.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/ProtocolExceptionTests.cs
@@ -1,97 +1,97 @@
-//-----------------------------------------------------------------------
-// <copyright file="ProtocolExceptionTests.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.Messaging {
- using System;
- using DotNetOpenAuth.Messaging;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
-
- [TestClass]
- public class ProtocolExceptionTests : TestBase {
- [TestMethod]
- public void CtorDefault() {
- ProtocolException ex = new ProtocolException();
- }
-
- [TestMethod]
- public void CtorWithTextMessage() {
- ProtocolException ex = new ProtocolException("message");
- Assert.AreEqual("message", ex.Message);
- }
-
- [TestMethod]
- public void CtorWithTextMessageAndInnerException() {
- Exception innerException = new Exception();
- ProtocolException ex = new ProtocolException("message", innerException);
- Assert.AreEqual("message", ex.Message);
- Assert.AreSame(innerException, ex.InnerException);
- }
-
- [TestMethod]
- public void CtorWithProtocolMessage() {
- IProtocolMessage request = new Mocks.TestDirectedMessage();
- Uri receiver = new Uri("http://receiver");
- ProtocolException ex = new ProtocolException("some error occurred", request, receiver);
- IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
- Assert.AreEqual("some error occurred", ex.Message);
- Assert.AreSame(receiver, msg.Recipient);
- Assert.AreEqual(request.ProtocolVersion, msg.ProtocolVersion);
- Assert.AreEqual(request.Transport, msg.Transport);
- msg.EnsureValidMessage();
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void CtorWithNullProtocolMessage() {
- new ProtocolException("message", null, new Uri("http://receiver"));
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void CtorWithNullReceiver() {
- new ProtocolException("message", new Mocks.TestDirectedMessage(MessageTransport.Indirect), null);
- }
-
- /// <summary>
- /// Tests that exceptions being sent as direct responses do not need an explicit receiver.
- /// </summary>
- [TestMethod]
- public void CtorUndirectedMessageWithNullReceiver() {
- IProtocolMessage request = new Mocks.TestDirectedMessage(MessageTransport.Direct);
- ProtocolException ex = new ProtocolException("message", request, null);
- IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
- Assert.IsNull(msg.Recipient);
- Assert.AreEqual(request.ProtocolVersion, msg.ProtocolVersion);
- Assert.AreEqual(request.Transport, msg.Transport);
- }
-
- [TestMethod, ExpectedException(typeof(InvalidOperationException))]
- public void ProtocolVersionWithoutMessage() {
- ProtocolException ex = new ProtocolException();
- IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
- var temp = msg.ProtocolVersion;
- }
-
- [TestMethod, ExpectedException(typeof(InvalidOperationException))]
- public void TransportWithoutMessage() {
- ProtocolException ex = new ProtocolException();
- IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
- var temp = msg.Transport;
- }
-
- [TestMethod, ExpectedException(typeof(InvalidOperationException))]
- public void RecipientWithoutMessage() {
- ProtocolException ex = new ProtocolException();
- IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
- var temp = msg.Recipient;
- }
-
- [TestMethod, ExpectedException(typeof(InvalidOperationException))]
- public void EnsureValidMessageWithoutMessage() {
- ProtocolException ex = new ProtocolException();
- IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
- msg.EnsureValidMessage();
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ProtocolExceptionTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.Messaging {
+ using System;
+ using DotNetOpenAuth.Messaging;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class ProtocolExceptionTests : TestBase {
+ [TestMethod]
+ public void CtorDefault() {
+ ProtocolException ex = new ProtocolException();
+ }
+
+ [TestMethod]
+ public void CtorWithTextMessage() {
+ ProtocolException ex = new ProtocolException("message");
+ Assert.AreEqual("message", ex.Message);
+ }
+
+ [TestMethod]
+ public void CtorWithTextMessageAndInnerException() {
+ Exception innerException = new Exception();
+ ProtocolException ex = new ProtocolException("message", innerException);
+ Assert.AreEqual("message", ex.Message);
+ Assert.AreSame(innerException, ex.InnerException);
+ }
+
+ [TestMethod]
+ public void CtorWithProtocolMessage() {
+ IProtocolMessage request = new Mocks.TestDirectedMessage();
+ Uri receiver = new Uri("http://receiver");
+ ProtocolException ex = new ProtocolException("some error occurred", request, receiver);
+ IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
+ Assert.AreEqual("some error occurred", ex.Message);
+ Assert.AreSame(receiver, msg.Recipient);
+ Assert.AreEqual(request.Version, msg.Version);
+ Assert.AreEqual(request.Transport, msg.Transport);
+ msg.EnsureValidMessage();
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void CtorWithNullProtocolMessage() {
+ new ProtocolException("message", null, new Uri("http://receiver"));
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void CtorWithNullReceiver() {
+ new ProtocolException("message", new Mocks.TestDirectedMessage(MessageTransport.Indirect), null);
+ }
+
+ /// <summary>
+ /// Tests that exceptions being sent as direct responses do not need an explicit receiver.
+ /// </summary>
+ [TestMethod]
+ public void CtorUndirectedMessageWithNullReceiver() {
+ IProtocolMessage request = new Mocks.TestDirectedMessage(MessageTransport.Direct);
+ ProtocolException ex = new ProtocolException("message", request, null);
+ IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
+ Assert.IsNull(msg.Recipient);
+ Assert.AreEqual(request.Version, msg.Version);
+ Assert.AreEqual(request.Transport, msg.Transport);
+ }
+
+ [TestMethod, ExpectedException(typeof(InvalidOperationException))]
+ public void ProtocolVersionWithoutMessage() {
+ ProtocolException ex = new ProtocolException();
+ IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
+ var temp = msg.Version;
+ }
+
+ [TestMethod, ExpectedException(typeof(InvalidOperationException))]
+ public void TransportWithoutMessage() {
+ ProtocolException ex = new ProtocolException();
+ IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
+ var temp = msg.Transport;
+ }
+
+ [TestMethod, ExpectedException(typeof(InvalidOperationException))]
+ public void RecipientWithoutMessage() {
+ ProtocolException ex = new ProtocolException();
+ IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
+ var temp = msg.Recipient;
+ }
+
+ [TestMethod, ExpectedException(typeof(InvalidOperationException))]
+ public void EnsureValidMessageWithoutMessage() {
+ ProtocolException ex = new ProtocolException();
+ IDirectedProtocolMessage msg = (IDirectedProtocolMessage)ex;
+ msg.EnsureValidMessage();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/Mocks/TestBaseMessage.cs b/src/DotNetOpenAuth.Test/Mocks/TestBaseMessage.cs
index 212907f..49b11bf 100644
--- a/src/DotNetOpenAuth.Test/Mocks/TestBaseMessage.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/TestBaseMessage.cs
@@ -1,65 +1,65 @@
-//-----------------------------------------------------------------------
-// <copyright file="TestBaseMessage.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.Mocks {
- using System;
- using System.Collections.Generic;
- using System.Runtime.Serialization;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Reflection;
-
- internal interface IBaseMessageExplicitMembers {
- string ExplicitProperty { get; set; }
- }
-
- internal class TestBaseMessage : IProtocolMessage, IBaseMessageExplicitMembers {
- private Dictionary<string, string> extraData = new Dictionary<string, string>();
- private bool incoming;
-
- [MessagePart("age", IsRequired = true)]
- public int Age { get; set; }
-
- [MessagePart]
- public string Name { get; set; }
-
- [MessagePart("explicit")]
- string IBaseMessageExplicitMembers.ExplicitProperty { get; set; }
-
- Version IProtocolMessage.ProtocolVersion {
- get { return new Version(1, 0); }
- }
-
- MessageProtections IProtocolMessage.RequiredProtection {
- get { return MessageProtections.None; }
- }
-
- MessageTransport IProtocolMessage.Transport {
- get { return MessageTransport.Indirect; }
- }
-
- IDictionary<string, string> IProtocolMessage.ExtraData {
- get { return this.extraData; }
- }
-
- bool IProtocolMessage.Incoming {
- get { return this.incoming; }
- }
-
- internal string PrivatePropertyAccessor {
- get { return this.PrivateProperty; }
- set { this.PrivateProperty = value; }
- }
-
- [MessagePart("private")]
- private string PrivateProperty { get; set; }
-
- void IProtocolMessage.EnsureValidMessage() { }
-
- internal void SetAsIncoming() {
- this.incoming = true;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="TestBaseMessage.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.Mocks {
+ using System;
+ using System.Collections.Generic;
+ using System.Runtime.Serialization;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Reflection;
+
+ internal interface IBaseMessageExplicitMembers {
+ string ExplicitProperty { get; set; }
+ }
+
+ internal class TestBaseMessage : IProtocolMessage, IBaseMessageExplicitMembers {
+ private Dictionary<string, string> extraData = new Dictionary<string, string>();
+ private bool incoming;
+
+ [MessagePart("age", IsRequired = true)]
+ public int Age { get; set; }
+
+ [MessagePart]
+ public string Name { get; set; }
+
+ [MessagePart("explicit")]
+ string IBaseMessageExplicitMembers.ExplicitProperty { get; set; }
+
+ Version IMessage.Version {
+ get { return new Version(1, 0); }
+ }
+
+ MessageProtections IProtocolMessage.RequiredProtection {
+ get { return MessageProtections.None; }
+ }
+
+ MessageTransport IProtocolMessage.Transport {
+ get { return MessageTransport.Indirect; }
+ }
+
+ IDictionary<string, string> IMessage.ExtraData {
+ get { return this.extraData; }
+ }
+
+ bool IMessage.Incoming {
+ get { return this.incoming; }
+ }
+
+ internal string PrivatePropertyAccessor {
+ get { return this.PrivateProperty; }
+ set { this.PrivateProperty = value; }
+ }
+
+ [MessagePart("private")]
+ private string PrivateProperty { get; set; }
+
+ void IMessage.EnsureValidMessage() { }
+
+ internal void SetAsIncoming() {
+ this.incoming = true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/Mocks/TestMessage.cs b/src/DotNetOpenAuth.Test/Mocks/TestMessage.cs
index 4a168a1..a2f5328 100644
--- a/src/DotNetOpenAuth.Test/Mocks/TestMessage.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/TestMessage.cs
@@ -1,82 +1,82 @@
-//-----------------------------------------------------------------------
-// <copyright file="TestMessage.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.Mocks {
- using System;
- using System.Collections.Generic;
- using System.Runtime.Serialization;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Reflection;
-
- internal abstract class TestMessage : IDirectResponseProtocolMessage {
- private MessageTransport transport;
- private Dictionary<string, string> extraData = new Dictionary<string, string>();
- private bool incoming;
-
- protected TestMessage()
- : this(MessageTransport.Direct) {
- }
-
- protected TestMessage(MessageTransport transport) {
- this.transport = transport;
- }
-
- [MessagePart("age", IsRequired = true)]
- public int Age { get; set; }
- [MessagePart("Name")]
- public string Name { get; set; }
- [MessagePart]
- public string EmptyMember { get; set; }
- [MessagePart(null)] // null name tests that Location is still the name.
- public Uri Location { get; set; }
- [MessagePart(IsRequired = true)]
- public DateTime Timestamp { get; set; }
-
- #region IProtocolMessage Properties
-
- Version IProtocolMessage.ProtocolVersion {
- get { return new Version(1, 0); }
- }
-
- MessageProtections IProtocolMessage.RequiredProtection {
- get { return MessageProtections.None; }
- }
-
- MessageTransport IProtocolMessage.Transport {
- get { return this.transport; }
- }
-
- IDictionary<string, string> IProtocolMessage.ExtraData {
- get { return this.extraData; }
- }
-
- bool IProtocolMessage.Incoming {
- get { return this.incoming; }
- }
-
- #endregion
-
- #region IDirectResponseProtocolMessage Members
-
- public IDirectedProtocolMessage OriginatingRequest { get; set; }
-
- #endregion
-
- #region IProtocolMessage Methods
-
- void IProtocolMessage.EnsureValidMessage() {
- if (this.EmptyMember != null || this.Age < 0) {
- throw new ProtocolException();
- }
- }
-
- #endregion
-
- internal void SetAsIncoming() {
- this.incoming = true;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="TestMessage.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.Mocks {
+ using System;
+ using System.Collections.Generic;
+ using System.Runtime.Serialization;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Reflection;
+
+ internal abstract class TestMessage : IDirectResponseProtocolMessage {
+ private MessageTransport transport;
+ private Dictionary<string, string> extraData = new Dictionary<string, string>();
+ private bool incoming;
+
+ protected TestMessage()
+ : this(MessageTransport.Direct) {
+ }
+
+ protected TestMessage(MessageTransport transport) {
+ this.transport = transport;
+ }
+
+ [MessagePart("age", IsRequired = true)]
+ public int Age { get; set; }
+ [MessagePart("Name")]
+ public string Name { get; set; }
+ [MessagePart]
+ public string EmptyMember { get; set; }
+ [MessagePart(null)] // null name tests that Location is still the name.
+ public Uri Location { get; set; }
+ [MessagePart(IsRequired = true)]
+ public DateTime Timestamp { get; set; }
+
+ #region IProtocolMessage Properties
+
+ Version IMessage.Version {
+ get { return new Version(1, 0); }
+ }
+
+ MessageProtections IProtocolMessage.RequiredProtection {
+ get { return MessageProtections.None; }
+ }
+
+ MessageTransport IProtocolMessage.Transport {
+ get { return this.transport; }
+ }
+
+ IDictionary<string, string> IMessage.ExtraData {
+ get { return this.extraData; }
+ }
+
+ bool IMessage.Incoming {
+ get { return this.incoming; }
+ }
+
+ #endregion
+
+ #region IDirectResponseProtocolMessage Members
+
+ public IDirectedProtocolMessage OriginatingRequest { get; set; }
+
+ #endregion
+
+ #region IMessage Methods
+
+ void IMessage.EnsureValidMessage() {
+ if (this.EmptyMember != null || this.Age < 0) {
+ throw new ProtocolException();
+ }
+ }
+
+ #endregion
+
+ internal void SetAsIncoming() {
+ this.incoming = true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs b/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs
index 8637cfd..1f54b32 100644
--- a/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs
@@ -1,188 +1,188 @@
-//-----------------------------------------------------------------------
-// <copyright file="AssociationHandshakeTests.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.OpenId {
- using System;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OpenId;
- using DotNetOpenAuth.OpenId.Messages;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
-
- [TestClass]
- public class AssociationHandshakeTests : OpenIdTestBase {
- [TestInitialize]
- public override void SetUp() {
- base.SetUp();
- }
-
- [TestMethod]
- public void AssociateUnencrypted() {
- this.ParameterizedAssociationTest(new Uri("https://host"));
- }
-
- [TestMethod]
- public void AssociateDiffieHellmanOverHttp() {
- this.ParameterizedAssociationTest(new Uri("http://host"));
- }
-
- [TestMethod, Ignore]
- public void AssociateDiffieHellmanOverHttps() {
- // TODO: test the RP and OP agreeing to use Diffie-Hellman over HTTPS.
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Verifies that the RP and OP can renegotiate an association type if the RP's
- /// initial request for an association is for a type the OP doesn't support.
- /// </summary>
- [TestMethod, Ignore]
- public void AssociateRenegotiateBitLength() {
- // TODO: test where the RP asks for an association type that the OP doesn't support
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Verifies that the RP cannot get caught in an infinite loop if a bad OP
- /// keeps sending it association retry messages.
- /// </summary>
- [TestMethod, Ignore]
- public void AssociateRenegotiateBitLengthRPStopsAfterOneRetry() {
- // TODO: code here
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Verifies security settings limit RP's initial associate request
- /// </summary>
- [TestMethod, Ignore]
- public void AssociateRequestDeterminedBySecuritySettings() {
- // TODO: Code here
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Verifies security settings limit RP's acceptance of OP's counter-suggestion
- /// </summary>
- [TestMethod, Ignore]
- public void AssociateRenegotiateLimitedByRPSecuritySettings() {
- // TODO: Code here
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Verifies security settings limit OP's set of acceptable association types.
- /// </summary>
- [TestMethod, Ignore]
- public void AssociateLimitedByOPSecuritySettings() {
- // TODO: Code here
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Verifies the RP can recover with no association after receiving an
- /// associate error response from the OP when no suggested association
- /// type is included.
- /// </summary>
- [TestMethod, Ignore]
- public void AssociateContinueAfterOpenIdError() {
- // TODO: Code here
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Verifies that the RP can recover from an invalid or non-existent
- /// response from the OP, for example in the HTTP timeout case.
- /// </summary>
- [TestMethod, Ignore]
- public void AssociateContinueAfterHttpError() {
- // TODO: Code here
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Runs a parameterized association flow test using all supported OpenID versions.
- /// </summary>
- /// <param name="opEndpoint">The OP endpoint to simulate using.</param>
- private void ParameterizedAssociationTest(Uri opEndpoint) {
- foreach (Protocol protocol in Protocol.AllPracticalVersions) {
- var endpoint = new ProviderEndpointDescription(opEndpoint, protocol.Version);
- var associationType = protocol.Version.Major < 2 ? protocol.Args.SignatureAlgorithm.HMAC_SHA1 : protocol.Args.SignatureAlgorithm.HMAC_SHA256;
- this.ParameterizedAssociationTest(endpoint, associationType);
- }
- }
-
- /// <summary>
- /// Runs a parameterized association flow test.
- /// </summary>
- /// <param name="opDescription">
- /// The description of the Provider that the relying party uses to formulate the request.
- /// The specific host is not used, but the scheme is significant.
- /// </param>
- /// <param name="expectedAssociationType">
- /// The value of the openid.assoc_type parameter expected,
- /// or null if a failure is anticipated.
- /// </param>
- private void ParameterizedAssociationTest(
- ProviderEndpointDescription opDescription,
- string expectedAssociationType) {
- Protocol protocol = Protocol.Lookup(opDescription.ProtocolVersion);
- bool expectSuccess = expectedAssociationType != null;
- bool expectDiffieHellman = !opDescription.Endpoint.IsTransportSecure();
- Association rpAssociation = null, opAssociation;
- AssociateSuccessfulResponse associateSuccessfulResponse = null;
- AssociateUnsuccessfulResponse associateUnsuccessfulResponse = null;
- OpenIdCoordinator coordinator = new OpenIdCoordinator(
- rp => {
- rp.SecuritySettings = this.RelyingPartySecuritySettings;
- rpAssociation = rp.GetAssociation(opDescription);
- },
- op => {
- op.SecuritySettings = this.ProviderSecuritySettings;
- op.AutoRespond();
- });
- coordinator.IncomingMessageFilter = message => {
- Assert.AreSame(opDescription.ProtocolVersion, message.ProtocolVersion, "The message was recognized as version {0} but was expected to be {1}.", message.ProtocolVersion, opDescription.ProtocolVersion);
- var associateSuccess = message as AssociateSuccessfulResponse;
- var associateFailed = message as AssociateUnsuccessfulResponse;
- if (associateSuccess != null) {
- associateSuccessfulResponse = associateSuccess;
- }
- if (associateFailed != null) {
- associateUnsuccessfulResponse = associateFailed;
- }
- };
- coordinator.OutgoingMessageFilter = message => {
- Assert.AreSame(opDescription.ProtocolVersion, message.ProtocolVersion, "The message was for version {0} but was expected to be for {1}.", message.ProtocolVersion, opDescription.ProtocolVersion);
- };
- coordinator.Run();
-
- if (expectSuccess) {
- Assert.IsNotNull(rpAssociation);
- Assert.AreSame(rpAssociation, coordinator.RelyingParty.AssociationStore.GetAssociation(opDescription.Endpoint, rpAssociation.Handle));
- opAssociation = coordinator.Provider.AssociationStore.GetAssociation(AssociationRelyingPartyType.Smart, rpAssociation.Handle);
- Assert.IsNotNull(opAssociation, "The Provider should have stored the association.");
-
- Assert.AreEqual(opAssociation.Handle, rpAssociation.Handle);
- Assert.AreEqual(expectedAssociationType, rpAssociation.GetAssociationType(protocol));
- Assert.AreEqual(expectedAssociationType, opAssociation.GetAssociationType(protocol));
- Assert.IsTrue(Math.Abs(opAssociation.SecondsTillExpiration - rpAssociation.SecondsTillExpiration) < 60);
- Assert.IsTrue(MessagingUtilities.AreEquivalent(opAssociation.SecretKey, rpAssociation.SecretKey));
-
- if (expectDiffieHellman) {
- Assert.IsInstanceOfType(associateSuccessfulResponse, typeof(AssociateDiffieHellmanResponse));
- var diffieHellmanResponse = (AssociateDiffieHellmanResponse)associateSuccessfulResponse;
- Assert.IsFalse(MessagingUtilities.AreEquivalent(diffieHellmanResponse.EncodedMacKey, rpAssociation.SecretKey), "Key should have been encrypted.");
- } else {
- Assert.IsInstanceOfType(associateSuccessfulResponse, typeof(AssociateUnencryptedResponse));
- var unencryptedResponse = (AssociateUnencryptedResponse)associateSuccessfulResponse;
- }
- } else {
- Assert.IsNull(coordinator.RelyingParty.AssociationStore.GetAssociation(opDescription.Endpoint));
- Assert.IsNull(coordinator.Provider.AssociationStore.GetAssociation(AssociationRelyingPartyType.Smart));
- }
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="AssociationHandshakeTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId {
+ using System;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.Messages;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class AssociationHandshakeTests : OpenIdTestBase {
+ [TestInitialize]
+ public override void SetUp() {
+ base.SetUp();
+ }
+
+ [TestMethod]
+ public void AssociateUnencrypted() {
+ this.ParameterizedAssociationTest(new Uri("https://host"));
+ }
+
+ [TestMethod]
+ public void AssociateDiffieHellmanOverHttp() {
+ this.ParameterizedAssociationTest(new Uri("http://host"));
+ }
+
+ [TestMethod, Ignore]
+ public void AssociateDiffieHellmanOverHttps() {
+ // TODO: test the RP and OP agreeing to use Diffie-Hellman over HTTPS.
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Verifies that the RP and OP can renegotiate an association type if the RP's
+ /// initial request for an association is for a type the OP doesn't support.
+ /// </summary>
+ [TestMethod, Ignore]
+ public void AssociateRenegotiateBitLength() {
+ // TODO: test where the RP asks for an association type that the OP doesn't support
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Verifies that the RP cannot get caught in an infinite loop if a bad OP
+ /// keeps sending it association retry messages.
+ /// </summary>
+ [TestMethod, Ignore]
+ public void AssociateRenegotiateBitLengthRPStopsAfterOneRetry() {
+ // TODO: code here
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Verifies security settings limit RP's initial associate request
+ /// </summary>
+ [TestMethod, Ignore]
+ public void AssociateRequestDeterminedBySecuritySettings() {
+ // TODO: Code here
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Verifies security settings limit RP's acceptance of OP's counter-suggestion
+ /// </summary>
+ [TestMethod, Ignore]
+ public void AssociateRenegotiateLimitedByRPSecuritySettings() {
+ // TODO: Code here
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Verifies security settings limit OP's set of acceptable association types.
+ /// </summary>
+ [TestMethod, Ignore]
+ public void AssociateLimitedByOPSecuritySettings() {
+ // TODO: Code here
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Verifies the RP can recover with no association after receiving an
+ /// associate error response from the OP when no suggested association
+ /// type is included.
+ /// </summary>
+ [TestMethod, Ignore]
+ public void AssociateContinueAfterOpenIdError() {
+ // TODO: Code here
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Verifies that the RP can recover from an invalid or non-existent
+ /// response from the OP, for example in the HTTP timeout case.
+ /// </summary>
+ [TestMethod, Ignore]
+ public void AssociateContinueAfterHttpError() {
+ // TODO: Code here
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Runs a parameterized association flow test using all supported OpenID versions.
+ /// </summary>
+ /// <param name="opEndpoint">The OP endpoint to simulate using.</param>
+ private void ParameterizedAssociationTest(Uri opEndpoint) {
+ foreach (Protocol protocol in Protocol.AllPracticalVersions) {
+ var endpoint = new ProviderEndpointDescription(opEndpoint, protocol.Version);
+ var associationType = protocol.Version.Major < 2 ? protocol.Args.SignatureAlgorithm.HMAC_SHA1 : protocol.Args.SignatureAlgorithm.HMAC_SHA256;
+ this.ParameterizedAssociationTest(endpoint, associationType);
+ }
+ }
+
+ /// <summary>
+ /// Runs a parameterized association flow test.
+ /// </summary>
+ /// <param name="opDescription">
+ /// The description of the Provider that the relying party uses to formulate the request.
+ /// The specific host is not used, but the scheme is significant.
+ /// </param>
+ /// <param name="expectedAssociationType">
+ /// The value of the openid.assoc_type parameter expected,
+ /// or null if a failure is anticipated.
+ /// </param>
+ private void ParameterizedAssociationTest(
+ ProviderEndpointDescription opDescription,
+ string expectedAssociationType) {
+ Protocol protocol = Protocol.Lookup(opDescription.ProtocolVersion);
+ bool expectSuccess = expectedAssociationType != null;
+ bool expectDiffieHellman = !opDescription.Endpoint.IsTransportSecure();
+ Association rpAssociation = null, opAssociation;
+ AssociateSuccessfulResponse associateSuccessfulResponse = null;
+ AssociateUnsuccessfulResponse associateUnsuccessfulResponse = null;
+ OpenIdCoordinator coordinator = new OpenIdCoordinator(
+ rp => {
+ rp.SecuritySettings = this.RelyingPartySecuritySettings;
+ rpAssociation = rp.GetAssociation(opDescription);
+ },
+ op => {
+ op.SecuritySettings = this.ProviderSecuritySettings;
+ op.AutoRespond();
+ });
+ coordinator.IncomingMessageFilter = message => {
+ Assert.AreSame(opDescription.ProtocolVersion, message.Version, "The message was recognized as version {0} but was expected to be {1}.", message.Version, opDescription.ProtocolVersion);
+ var associateSuccess = message as AssociateSuccessfulResponse;
+ var associateFailed = message as AssociateUnsuccessfulResponse;
+ if (associateSuccess != null) {
+ associateSuccessfulResponse = associateSuccess;
+ }
+ if (associateFailed != null) {
+ associateUnsuccessfulResponse = associateFailed;
+ }
+ };
+ coordinator.OutgoingMessageFilter = message => {
+ Assert.AreSame(opDescription.ProtocolVersion, message.Version, "The message was for version {0} but was expected to be for {1}.", message.Version, opDescription.ProtocolVersion);
+ };
+ coordinator.Run();
+
+ if (expectSuccess) {
+ Assert.IsNotNull(rpAssociation);
+ Assert.AreSame(rpAssociation, coordinator.RelyingParty.AssociationStore.GetAssociation(opDescription.Endpoint, rpAssociation.Handle));
+ opAssociation = coordinator.Provider.AssociationStore.GetAssociation(AssociationRelyingPartyType.Smart, rpAssociation.Handle);
+ Assert.IsNotNull(opAssociation, "The Provider should have stored the association.");
+
+ Assert.AreEqual(opAssociation.Handle, rpAssociation.Handle);
+ Assert.AreEqual(expectedAssociationType, rpAssociation.GetAssociationType(protocol));
+ Assert.AreEqual(expectedAssociationType, opAssociation.GetAssociationType(protocol));
+ Assert.IsTrue(Math.Abs(opAssociation.SecondsTillExpiration - rpAssociation.SecondsTillExpiration) < 60);
+ Assert.IsTrue(MessagingUtilities.AreEquivalent(opAssociation.SecretKey, rpAssociation.SecretKey));
+
+ if (expectDiffieHellman) {
+ Assert.IsInstanceOfType(associateSuccessfulResponse, typeof(AssociateDiffieHellmanResponse));
+ var diffieHellmanResponse = (AssociateDiffieHellmanResponse)associateSuccessfulResponse;
+ Assert.IsFalse(MessagingUtilities.AreEquivalent(diffieHellmanResponse.EncodedMacKey, rpAssociation.SecretKey), "Key should have been encrypted.");
+ } else {
+ Assert.IsInstanceOfType(associateSuccessfulResponse, typeof(AssociateUnencryptedResponse));
+ var unencryptedResponse = (AssociateUnencryptedResponse)associateSuccessfulResponse;
+ }
+ } else {
+ Assert.IsNull(coordinator.RelyingParty.AssociationStore.GetAssociation(opDescription.Endpoint));
+ Assert.IsNull(coordinator.Provider.AssociationStore.GetAssociation(AssociationRelyingPartyType.Smart));
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/Messages/IndirectSignedResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Messages/IndirectSignedResponseTests.cs
index 8900e76..f0a9cc2 100644
--- a/src/DotNetOpenAuth.Test/OpenId/Messages/IndirectSignedResponseTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/Messages/IndirectSignedResponseTests.cs
@@ -1,111 +1,111 @@
-//-----------------------------------------------------------------------
-// <copyright file="IndirectSignedResponseTests.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.OpenId.Messages {
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Bindings;
- using DotNetOpenAuth.OpenId;
- using DotNetOpenAuth.OpenId.ChannelElements;
- using DotNetOpenAuth.OpenId.Messages;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
-
- [TestClass]
- public class IndirectSignedResponseTests : OpenIdTestBase {
- private const string CreationDateString = "2005-05-15T17:11:51Z";
- private readonly DateTime creationDate = DateTime.Parse(CreationDateString, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
- private CheckIdRequest request;
- private IndirectSignedResponse response;
- private IndirectSignedResponse unsolicited;
- private Protocol protocol;
-
- [TestInitialize]
- public override void SetUp() {
- base.SetUp();
-
- this.protocol = Protocol.V20;
- this.request = new CheckIdRequest(this.protocol.Version, ProviderUri, false);
- this.request.ReturnTo = RPUri;
- this.response = new IndirectSignedResponse(this.request);
-
- this.unsolicited = new IndirectSignedResponse(this.protocol.Version, RPUri);
- }
-
- [TestMethod]
- public void CtorFromRequest() {
- Assert.AreEqual(this.protocol.Args.Mode.id_res, this.response.Mode);
- Assert.AreEqual(this.request.ProtocolVersion, this.response.ProtocolVersion);
- Assert.AreEqual(this.request.ReturnTo, this.response.Recipient);
- Assert.AreEqual(ProviderUri, this.response.ProviderEndpoint);
- Assert.IsTrue(DateTime.UtcNow - ((ITamperResistantOpenIdMessage)this.response).UtcCreationDate < TimeSpan.FromSeconds(5));
- }
-
- [TestMethod]
- public void CtorUnsolicited() {
- Assert.AreEqual(this.protocol.Args.Mode.id_res, this.unsolicited.Mode);
- Assert.AreEqual(this.protocol.Version, this.unsolicited.ProtocolVersion);
- Assert.AreEqual(RPUri, this.unsolicited.Recipient);
- Assert.IsTrue(DateTime.UtcNow - ((ITamperResistantOpenIdMessage)this.unsolicited).UtcCreationDate < TimeSpan.FromSeconds(5));
-
- Assert.IsNull(this.unsolicited.ProviderEndpoint);
- this.unsolicited.ProviderEndpoint = ProviderUri;
- Assert.AreEqual(ProviderUri, this.unsolicited.ProviderEndpoint);
- }
-
- [TestMethod]
- public void ResponseNonceSetter() {
- const string HybridValue = CreationDateString + "UNIQUE";
- var responseAccessor = IndirectSignedResponse_Accessor.AttachShadow(this.response);
- IReplayProtectedProtocolMessage responseReplay = this.response;
- responseAccessor.ResponseNonce = HybridValue;
- Assert.AreEqual(HybridValue, responseAccessor.ResponseNonce);
- Assert.AreEqual(this.creationDate, responseReplay.UtcCreationDate);
- Assert.AreEqual("UNIQUE", responseReplay.Nonce);
-
- responseAccessor.ResponseNonce = null;
- Assert.IsNull(responseReplay.Nonce);
- }
-
- [TestMethod]
- public void ResponseNonceGetter() {
- var responseAccessor = IndirectSignedResponse_Accessor.AttachShadow(this.response);
- IReplayProtectedProtocolMessage responseReplay = this.response;
- responseReplay.Nonce = "UnIqUe";
- responseReplay.UtcCreationDate = this.creationDate;
-
- Assert.AreEqual(CreationDateString + "UnIqUe", responseAccessor.ResponseNonce);
- Assert.AreEqual("UnIqUe", responseReplay.Nonce);
- Assert.AreEqual(this.creationDate, responseReplay.UtcCreationDate);
- }
-
- [TestMethod]
- public void UtcCreationDateConvertsToUniversal() {
- IReplayProtectedProtocolMessage responseReplay = this.response;
- DateTime local = DateTime.Parse("1982-01-01", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal);
- if (local.Kind != DateTimeKind.Local) {
- Assert.Inconclusive("Test cannot manage to create a local time.");
- }
-
- responseReplay.UtcCreationDate = local;
- DateTime utcCreationDate = responseReplay.UtcCreationDate;
- Assert.AreEqual(DateTimeKind.Utc, utcCreationDate.Kind, "Local time should have been converted to universal time.");
- Assert.AreNotEqual(local.Hour, utcCreationDate.Hour, "The hour was expected to change (unless local time _is_ UTC time for this PC!)");
-
- // Now try setting UTC time just to make sure it DOESN'T mangle the hour
- if (this.creationDate.Kind != DateTimeKind.Utc) {
- Assert.Inconclusive("We need a UTC datetime to set with.");
- }
- responseReplay.UtcCreationDate = this.creationDate;
- utcCreationDate = responseReplay.UtcCreationDate;
- Assert.AreEqual(DateTimeKind.Utc, utcCreationDate.Kind);
- Assert.AreEqual(this.creationDate.Hour, utcCreationDate.Hour, "The hour should match since both times are UTC time.");
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="IndirectSignedResponseTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.ChannelElements;
+ using DotNetOpenAuth.OpenId.Messages;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class IndirectSignedResponseTests : OpenIdTestBase {
+ private const string CreationDateString = "2005-05-15T17:11:51Z";
+ private readonly DateTime creationDate = DateTime.Parse(CreationDateString, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
+ private CheckIdRequest request;
+ private IndirectSignedResponse response;
+ private IndirectSignedResponse unsolicited;
+ private Protocol protocol;
+
+ [TestInitialize]
+ public override void SetUp() {
+ base.SetUp();
+
+ this.protocol = Protocol.V20;
+ this.request = new CheckIdRequest(this.protocol.Version, ProviderUri, false);
+ this.request.ReturnTo = RPUri;
+ this.response = new IndirectSignedResponse(this.request);
+
+ this.unsolicited = new IndirectSignedResponse(this.protocol.Version, RPUri);
+ }
+
+ [TestMethod]
+ public void CtorFromRequest() {
+ Assert.AreEqual(this.protocol.Args.Mode.id_res, this.response.Mode);
+ Assert.AreEqual(this.request.Version, this.response.Version);
+ Assert.AreEqual(this.request.ReturnTo, this.response.Recipient);
+ Assert.AreEqual(ProviderUri, this.response.ProviderEndpoint);
+ Assert.IsTrue(DateTime.UtcNow - ((ITamperResistantOpenIdMessage)this.response).UtcCreationDate < TimeSpan.FromSeconds(5));
+ }
+
+ [TestMethod]
+ public void CtorUnsolicited() {
+ Assert.AreEqual(this.protocol.Args.Mode.id_res, this.unsolicited.Mode);
+ Assert.AreEqual(this.protocol.Version, this.unsolicited.Version);
+ Assert.AreEqual(RPUri, this.unsolicited.Recipient);
+ Assert.IsTrue(DateTime.UtcNow - ((ITamperResistantOpenIdMessage)this.unsolicited).UtcCreationDate < TimeSpan.FromSeconds(5));
+
+ Assert.IsNull(this.unsolicited.ProviderEndpoint);
+ this.unsolicited.ProviderEndpoint = ProviderUri;
+ Assert.AreEqual(ProviderUri, this.unsolicited.ProviderEndpoint);
+ }
+
+ [TestMethod]
+ public void ResponseNonceSetter() {
+ const string HybridValue = CreationDateString + "UNIQUE";
+ var responseAccessor = IndirectSignedResponse_Accessor.AttachShadow(this.response);
+ IReplayProtectedProtocolMessage responseReplay = this.response;
+ responseAccessor.ResponseNonce = HybridValue;
+ Assert.AreEqual(HybridValue, responseAccessor.ResponseNonce);
+ Assert.AreEqual(this.creationDate, responseReplay.UtcCreationDate);
+ Assert.AreEqual("UNIQUE", responseReplay.Nonce);
+
+ responseAccessor.ResponseNonce = null;
+ Assert.IsNull(responseReplay.Nonce);
+ }
+
+ [TestMethod]
+ public void ResponseNonceGetter() {
+ var responseAccessor = IndirectSignedResponse_Accessor.AttachShadow(this.response);
+ IReplayProtectedProtocolMessage responseReplay = this.response;
+ responseReplay.Nonce = "UnIqUe";
+ responseReplay.UtcCreationDate = this.creationDate;
+
+ Assert.AreEqual(CreationDateString + "UnIqUe", responseAccessor.ResponseNonce);
+ Assert.AreEqual("UnIqUe", responseReplay.Nonce);
+ Assert.AreEqual(this.creationDate, responseReplay.UtcCreationDate);
+ }
+
+ [TestMethod]
+ public void UtcCreationDateConvertsToUniversal() {
+ IReplayProtectedProtocolMessage responseReplay = this.response;
+ DateTime local = DateTime.Parse("1982-01-01", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal);
+ if (local.Kind != DateTimeKind.Local) {
+ Assert.Inconclusive("Test cannot manage to create a local time.");
+ }
+
+ responseReplay.UtcCreationDate = local;
+ DateTime utcCreationDate = responseReplay.UtcCreationDate;
+ Assert.AreEqual(DateTimeKind.Utc, utcCreationDate.Kind, "Local time should have been converted to universal time.");
+ Assert.AreNotEqual(local.Hour, utcCreationDate.Hour, "The hour was expected to change (unless local time _is_ UTC time for this PC!)");
+
+ // Now try setting UTC time just to make sure it DOESN'T mangle the hour
+ if (this.creationDate.Kind != DateTimeKind.Utc) {
+ Assert.Inconclusive("We need a UTC datetime to set with.");
+ }
+ responseReplay.UtcCreationDate = this.creationDate;
+ utcCreationDate = responseReplay.UtcCreationDate;
+ Assert.AreEqual(DateTimeKind.Utc, utcCreationDate.Kind);
+ Assert.AreEqual(this.creationDate.Hour, utcCreationDate.Hour, "The hour should match since both times are UTC time.");
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/Messages/PositiveAssertionResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Messages/PositiveAssertionResponseTests.cs
index 164b46b..ee5f69f 100644
--- a/src/DotNetOpenAuth.Test/OpenId/Messages/PositiveAssertionResponseTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/Messages/PositiveAssertionResponseTests.cs
@@ -1,73 +1,73 @@
-//-----------------------------------------------------------------------
-// <copyright file="PositiveAssertionResponseTests.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Test.OpenId.Messages {
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Bindings;
- using DotNetOpenAuth.OpenId;
- using DotNetOpenAuth.OpenId.Messages;
- using Microsoft.VisualStudio.TestTools.UnitTesting;
-
- [TestClass]
- public class PositiveAssertionResponseTests : OpenIdTestBase {
- private const string CreationDateString = "2005-05-15T17:11:51Z";
- private readonly DateTime creationDate = DateTime.Parse(CreationDateString, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
- private CheckIdRequest request;
- private PositiveAssertionResponse response;
- private PositiveAssertionResponse unsolicited;
- private Protocol protocol;
-
- [TestInitialize]
- public override void SetUp() {
- base.SetUp();
-
- this.protocol = Protocol.V20;
- this.request = new CheckIdRequest(this.protocol.Version, ProviderUri, false);
- this.request.ReturnTo = RPUri;
- this.response = new PositiveAssertionResponse(this.request);
-
- this.unsolicited = new PositiveAssertionResponse(this.protocol.Version, RPUri);
- }
-
- [TestMethod]
- public void CtorFromRequest() {
- Assert.AreEqual(this.protocol.Args.Mode.id_res, this.response.Mode);
- Assert.AreEqual(this.request.ProtocolVersion, this.response.ProtocolVersion);
- Assert.AreEqual(this.request.ReturnTo, this.response.Recipient);
- Assert.AreEqual(ProviderUri, this.response.ProviderEndpoint);
- }
-
- [TestMethod]
- public void CtorUnsolicited() {
- Assert.AreEqual(this.protocol.Args.Mode.id_res, this.unsolicited.Mode);
- Assert.AreEqual(this.protocol.Version, this.unsolicited.ProtocolVersion);
- Assert.AreEqual(RPUri, this.unsolicited.Recipient);
-
- Assert.IsNull(this.unsolicited.ProviderEndpoint);
- this.unsolicited.ProviderEndpoint = ProviderUri;
- Assert.AreEqual(ProviderUri, this.unsolicited.ProviderEndpoint);
- }
-
- /// <summary>
- /// Verifies that local_id and claimed_id can either be null or specified.
- /// </summary>
- [TestMethod]
- public void ClaimedIdAndLocalIdSpecifiedIsValid() {
- this.response.LocalIdentifier = "http://local";
- this.response.ClaimedIdentifier = "http://claimedid";
- this.response.EnsureValidMessage();
-
- this.response.LocalIdentifier = null;
- this.response.ClaimedIdentifier = null;
- this.response.EnsureValidMessage();
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="PositiveAssertionResponseTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.Messages;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class PositiveAssertionResponseTests : OpenIdTestBase {
+ private const string CreationDateString = "2005-05-15T17:11:51Z";
+ private readonly DateTime creationDate = DateTime.Parse(CreationDateString, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
+ private CheckIdRequest request;
+ private PositiveAssertionResponse response;
+ private PositiveAssertionResponse unsolicited;
+ private Protocol protocol;
+
+ [TestInitialize]
+ public override void SetUp() {
+ base.SetUp();
+
+ this.protocol = Protocol.V20;
+ this.request = new CheckIdRequest(this.protocol.Version, ProviderUri, false);
+ this.request.ReturnTo = RPUri;
+ this.response = new PositiveAssertionResponse(this.request);
+
+ this.unsolicited = new PositiveAssertionResponse(this.protocol.Version, RPUri);
+ }
+
+ [TestMethod]
+ public void CtorFromRequest() {
+ Assert.AreEqual(this.protocol.Args.Mode.id_res, this.response.Mode);
+ Assert.AreEqual(this.request.Version, this.response.Version);
+ Assert.AreEqual(this.request.ReturnTo, this.response.Recipient);
+ Assert.AreEqual(ProviderUri, this.response.ProviderEndpoint);
+ }
+
+ [TestMethod]
+ public void CtorUnsolicited() {
+ Assert.AreEqual(this.protocol.Args.Mode.id_res, this.unsolicited.Mode);
+ Assert.AreEqual(this.protocol.Version, this.unsolicited.Version);
+ Assert.AreEqual(RPUri, this.unsolicited.Recipient);
+
+ Assert.IsNull(this.unsolicited.ProviderEndpoint);
+ this.unsolicited.ProviderEndpoint = ProviderUri;
+ Assert.AreEqual(ProviderUri, this.unsolicited.ProviderEndpoint);
+ }
+
+ /// <summary>
+ /// Verifies that local_id and claimed_id can either be null or specified.
+ /// </summary>
+ [TestMethod]
+ public void ClaimedIdAndLocalIdSpecifiedIsValid() {
+ this.response.LocalIdentifier = "http://local";
+ this.response.ClaimedIdentifier = "http://claimedid";
+ this.response.EnsureValidMessage();
+
+ this.response.LocalIdentifier = null;
+ this.response.ClaimedIdentifier = null;
+ this.response.EnsureValidMessage();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
index 0abfc50..c8644c5 100644
--- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj
+++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
@@ -1,286 +1,304 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <PropertyGroup>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProductVersion>9.0.30729</ProductVersion>
- <SchemaVersion>2.0</SchemaVersion>
- <ProjectGuid>{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}</ProjectGuid>
- <OutputType>Library</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>DotNetOpenAuth</RootNamespace>
- <AssemblyName>DotNetOpenAuth</AssemblyName>
- <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
- <FileAlignment>512</FileAlignment>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
- <DebugSymbols>true</DebugSymbols>
- <DebugType>full</DebugType>
- <Optimize>false</Optimize>
- <OutputPath>..\..\bin\Debug\</OutputPath>
- <DefineConstants>DEBUG;TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
- <DocumentationFile>..\..\bin\Debug\DotNetOpenAuth.xml</DocumentationFile>
- <RunCodeAnalysis>false</RunCodeAnalysis>
- <CodeAnalysisRules>-Microsoft.Design#CA1054;-Microsoft.Design#CA1056;-Microsoft.Design#CA1055</CodeAnalysisRules>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
- <DebugType>pdbonly</DebugType>
- <Optimize>true</Optimize>
- <OutputPath>..\..\bin\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
- <DocumentationFile>..\..\bin\debug\DotNetOpenAuth.xml</DocumentationFile>
- <RunCodeAnalysis>true</RunCodeAnalysis>
- <CodeAnalysisRules>-Microsoft.Design#CA1054;-Microsoft.Design#CA1056;-Microsoft.Design#CA1055</CodeAnalysisRules>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Sign)' == 'true' ">
- <SignAssembly>true</SignAssembly>
- <AssemblyOriginatorKeyFile>..\official-build-key.pfx</AssemblyOriginatorKeyFile>
- <DefineConstants>$(DefineConstants);StrongNameSigned</DefineConstants>
- </PropertyGroup>
- <ItemGroup>
- <Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\..\lib\log4net.dll</HintPath>
- </Reference>
- <Reference Include="System" />
- <Reference Include="System.configuration" />
- <Reference Include="System.Core">
- <RequiredTargetFramework>3.5</RequiredTargetFramework>
- </Reference>
- <Reference Include="System.Data" />
- <Reference Include="System.ServiceModel">
- <RequiredTargetFramework>3.0</RequiredTargetFramework>
- </Reference>
- <Reference Include="System.Web" />
- <Reference Include="System.XML" />
- <Reference Include="System.Xml.Linq">
- <RequiredTargetFramework>3.5</RequiredTargetFramework>
- </Reference>
- </ItemGroup>
- <ItemGroup>
- <Compile Include="Configuration\ProviderSection.cs" />
- <Compile Include="Configuration\ProviderSecuritySettingsElement.cs" />
- <Compile Include="Configuration\RelyingPartySection.cs" />
- <Compile Include="Configuration\RelyingPartySecuritySettingsElement.cs" />
- <Compile Include="Configuration\TypeConfigurationElement.cs" />
- <Compile Include="Configuration\UntrustedWebRequestSection.cs" />
- <Compile Include="Configuration\HostNameOrRegexCollection.cs" />
- <Compile Include="Configuration\HostNameElement.cs" />
- <Compile Include="Messaging\DirectWebResponse.cs" />
- <Compile Include="Messaging\IDirectResponseProtocolMessage.cs" />
- <Compile Include="Messaging\EmptyDictionary.cs" />
- <Compile Include="Messaging\EmptyEnumerator.cs" />
- <Compile Include="Messaging\EmptyList.cs" />
- <Compile Include="Messaging\ErrorUtilities.cs" />
- <Compile Include="Messaging\IDirectSslWebRequestHandler.cs" />
- <Compile Include="Messaging\InternalErrorException.cs" />
- <Compile Include="Messaging\Reflection\IMessagePartEncoder.cs" />
- <Compile Include="OAuth\ChannelElements\OAuthConsumerMessageFactory.cs" />
- <Compile Include="OAuth\ChannelElements\ITokenGenerator.cs" />
- <Compile Include="OAuth\ChannelElements\ITokenManager.cs" />
- <Compile Include="OAuth\ChannelElements\OAuthHttpMethodBindingElement.cs" />
- <Compile Include="OAuth\ChannelElements\PlaintextSigningBindingElement.cs" />
- <Compile Include="OAuth\ChannelElements\HmacSha1SigningBindingElement.cs" />
- <Compile Include="OAuth\ChannelElements\SigningBindingElementChain.cs" />
- <Compile Include="OAuth\ChannelElements\StandardTokenGenerator.cs" />
- <Compile Include="OAuth\ChannelElements\TokenType.cs" />
- <Compile Include="OAuth\ConsumerBase.cs" />
- <Compile Include="OAuth\DesktopConsumer.cs" />
- <Compile Include="GlobalSuppressions.cs" />
- <Compile Include="OAuth\Messages\ITokenSecretContainingMessage.cs" />
- <Compile Include="Messaging\ChannelEventArgs.cs" />
- <Compile Include="Messaging\ITamperProtectionChannelBindingElement.cs" />
- <Compile Include="OAuth\OAuthStrings.Designer.cs">
- <AutoGen>True</AutoGen>
- <DesignTime>True</DesignTime>
- <DependentUpon>OAuthStrings.resx</DependentUpon>
- </Compile>
- <Compile Include="OAuth\ServiceProviderDescription.cs" />
- <Compile Include="OAuth\Messages\ITokenContainingMessage.cs" />
- <Compile Include="OAuth\Messages\SignedMessageBase.cs" />
- <Compile Include="Messaging\Bindings\NonceMemoryStore.cs" />
- <Compile Include="OAuth\ChannelElements\SigningBindingElementBase.cs" />
- <Compile Include="OAuth\WebConsumer.cs" />
- <Compile Include="Messaging\IDirectWebRequestHandler.cs" />
- <Compile Include="OAuth\ChannelElements\ITamperResistantOAuthMessage.cs" />
- <Compile Include="OAuth\Messages\MessageBase.cs" />
- <Compile Include="OAuth\Messages\AuthorizedTokenRequest.cs" />
- <Compile Include="Messaging\Bindings\INonceStore.cs" />
- <Compile Include="Messaging\Bindings\StandardReplayProtectionBindingElement.cs" />
- <Compile Include="Messaging\MessagePartAttribute.cs" />
- <Compile Include="Messaging\MessageProtections.cs" />
- <Compile Include="Messaging\IChannelBindingElement.cs" />
- <Compile Include="Messaging\Bindings\ReplayedMessageException.cs" />
- <Compile Include="Messaging\Bindings\ExpiredMessageException.cs" />
- <Compile Include="Messaging\Bindings\InvalidSignatureException.cs" />
- <Compile Include="Messaging\Bindings\IReplayProtectedProtocolMessage.cs" />
- <Compile Include="Messaging\Bindings\IExpiringProtocolMessage.cs" />
- <Compile Include="OAuth\Messages\AccessProtectedResourceRequest.cs" />
- <Compile Include="OAuth\Messages\AuthorizedTokenResponse.cs" />
- <Compile Include="OAuth\Messages\UserAuthorizationResponse.cs" />
- <Compile Include="OAuth\Messages\UserAuthorizationRequest.cs" />
- <Compile Include="OAuth\Messages\UnauthorizedTokenResponse.cs" />
- <Compile Include="Messaging\Channel.cs" />
- <Compile Include="Messaging\HttpRequestInfo.cs" />
- <Compile Include="Messaging\IDirectedProtocolMessage.cs" />
- <Compile Include="Messaging\IMessageFactory.cs" />
- <Compile Include="Messaging\ITamperResistantProtocolMessage.cs" />
- <Compile Include="Messaging\MessageSerializer.cs" />
- <Compile Include="Messaging\MessagingStrings.Designer.cs">
- <AutoGen>True</AutoGen>
- <DesignTime>True</DesignTime>
- <DependentUpon>MessagingStrings.resx</DependentUpon>
- </Compile>
- <Compile Include="Messaging\MessagingUtilities.cs" />
- <Compile Include="Messaging\Bindings\StandardExpirationBindingElement.cs" />
- <Compile Include="Messaging\Reflection\ValueMapping.cs" />
- <Compile Include="Messaging\Reflection\MessageDescription.cs" />
- <Compile Include="Messaging\Reflection\MessageDictionary.cs" />
- <Compile Include="Messaging\Reflection\MessagePart.cs" />
- <Compile Include="Messaging\UnprotectedMessageException.cs" />
- <Compile Include="OAuth\ChannelElements\OAuthChannel.cs" />
- <Compile Include="Messaging\UserAgentResponse.cs" />
- <Compile Include="Messaging\IProtocolMessage.cs" />
- <Compile Include="Logger.cs" />
- <Compile Include="Loggers\ILog.cs" />
- <Compile Include="Loggers\Log4NetLogger.cs" />
- <Compile Include="Loggers\NoOpLogger.cs" />
- <Compile Include="Loggers\TraceLogger.cs" />
- <Compile Include="Messaging\HttpDeliveryMethods.cs" />
- <Compile Include="Messaging\MessageTransport.cs" />
- <Compile Include="OAuth\ChannelElements\OAuthServiceProviderMessageFactory.cs" />
- <Compile Include="Messaging\ProtocolException.cs" />
- <Compile Include="OpenId\Association.cs" />
- <Compile Include="OpenId\AssociationMemoryStore.cs" />
- <Compile Include="OpenId\Associations.cs" />
- <Compile Include="OpenId\ChannelElements\ITamperResistantOpenIdMessage.cs" />
- <Compile Include="OpenId\ChannelElements\OriginalStringUriEncoder.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\Configuration.cs" />
- <Compile Include="OpenId\Identifier.cs" />
- <Compile Include="OpenId\Messages\CheckAuthenticationRequest.cs" />
- <Compile Include="OpenId\Messages\CheckAuthenticationResponse.cs" />
- <Compile Include="OpenId\Messages\CheckIdRequest.cs" />
- <Compile Include="OpenId\Messages\IndirectResponseBase.cs" />
- <Compile Include="OpenId\Messages\IndirectSignedResponse.cs" />
- <Compile Include="OpenId\Messages\NegativeAssertionResponse.cs" />
- <Compile Include="OpenId\Messages\PositiveAssertionResponse.cs" />
- <Compile Include="OpenId\Messages\SignedResponseRequest.cs" />
- <Compile Include="OpenId\NoDiscoveryIdentifier.cs" />
- <Compile Include="OpenId\Realm.cs" />
- <Compile Include="OpenId\RelyingPartyDescription.cs" />
- <Compile Include="OpenId\DiffieHellmanUtilities.cs" />
- <Compile Include="OpenId\DiffieHellman\DHKeyGeneration.cs" />
- <Compile Include="OpenId\DiffieHellman\DHParameters.cs" />
- <Compile Include="OpenId\DiffieHellman\DiffieHellman.cs" />
- <Compile Include="OpenId\DiffieHellman\DiffieHellmanManaged.cs" />
- <Compile Include="OpenId\DiffieHellman\mono\BigInteger.cs" />
- <Compile Include="OpenId\DiffieHellman\mono\ConfidenceFactor.cs" />
- <Compile Include="OpenId\DiffieHellman\mono\NextPrimeFinder.cs" />
- <Compile Include="OpenId\DiffieHellman\mono\PrimalityTests.cs" />
- <Compile Include="OpenId\DiffieHellman\mono\PrimeGeneratorBase.cs" />
- <Compile Include="OpenId\DiffieHellman\mono\SequentialSearchPrimeGeneratorBase.cs" />
- <Compile Include="OpenId\HmacShaAssociation.cs" />
- <Compile Include="OpenId\IAssociationStore.cs" />
- <Compile Include="OpenId\Messages\AssociateUnencryptedRequest.cs" />
- <Compile Include="OpenId\Provider\OpenIdProvider.cs" />
- <Compile Include="OpenId\Messages\AssociateDiffieHellmanRequest.cs" />
- <Compile Include="OpenId\Messages\AssociateDiffieHellmanResponse.cs" />
- <Compile Include="OpenId\Messages\AssociateRequest.cs" />
- <Compile Include="OpenId\Messages\AssociateSuccessfulResponse.cs" />
- <Compile Include="OpenId\Messages\AssociateUnencryptedResponse.cs" />
- <Compile Include="OpenId\Messages\AssociateUnsuccessfulResponse.cs" />
- <Compile Include="OpenId\Messages\IndirectErrorResponse.cs" />
- <Compile Include="OpenId\Messages\DirectErrorResponse.cs" />
- <Compile Include="OpenId\Messages\RequestBase.cs" />
- <Compile Include="OpenId\Messages\DirectResponseBase.cs" />
- <Compile Include="OpenId\RelyingParty\IProviderEndpoint.cs" />
- <Compile Include="OpenId\RelyingParty\IXrdsProviderEndpoint.cs" />
- <Compile Include="OpenId\RelyingParty\OpenIdRelyingParty.cs" />
- <Compile Include="OpenId\OpenIdStrings.Designer.cs">
- <DependentUpon>OpenIdStrings.resx</DependentUpon>
- <DesignTime>True</DesignTime>
- <AutoGen>True</AutoGen>
- </Compile>
- <Compile Include="OpenId\Protocol.cs" />
- <Compile Include="OpenId\ProviderDescription.cs" />
- <Compile Include="OpenId\Provider\ProviderSecuritySettings.cs" />
- <Compile Include="OpenId\RelyingParty\RelyingPartySecuritySettings.cs" />
- <Compile Include="OpenId\RelyingParty\ServiceEndpoint.cs" />
- <Compile Include="OpenId\OpenIdXrdsHelper.cs" />
- <Compile Include="OpenId\SecuritySettings.cs" />
- <Compile Include="Messaging\UntrustedWebRequestHandler.cs" />
- <Compile Include="OpenId\UriIdentifier.cs" />
- <Compile Include="OpenId\XriIdentifier.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="OAuth\Messages\UnauthorizedTokenRequest.cs" />
- <Compile Include="OAuth\ChannelElements\RsaSha1SigningBindingElement.cs" />
- <Compile Include="Messaging\StandardWebRequestHandler.cs" />
- <Compile Include="Messaging\MessageReceivingEndpoint.cs" />
- <Compile Include="Util.cs" />
- <Compile Include="OAuth\Protocol.cs" />
- <Compile Include="OAuth\ServiceProvider.cs" />
- <Compile Include="Strings.Designer.cs">
- <AutoGen>True</AutoGen>
- <DesignTime>True</DesignTime>
- <DependentUpon>Strings.resx</DependentUpon>
- </Compile>
- <Compile Include="UriUtil.cs" />
- <Compile Include="Xrds\XrdsStrings.Designer.cs">
- <AutoGen>True</AutoGen>
- <DesignTime>True</DesignTime>
- <DependentUpon>XrdsStrings.resx</DependentUpon>
- </Compile>
- <Compile Include="Yadis\ContentTypes.cs" />
- <Compile Include="Yadis\DiscoveryResult.cs" />
- <Compile Include="Yadis\HtmlParser.cs" />
- <Compile Include="Xrds\ServiceElement.cs" />
- <Compile Include="Xrds\TypeElement.cs" />
- <Compile Include="Xrds\UriElement.cs" />
- <Compile Include="Xrds\XrdElement.cs" />
- <Compile Include="Xrds\XrdsDocument.cs" />
- <Compile Include="Xrds\XrdsNode.cs" />
- <Compile Include="Yadis\Yadis.cs" />
- </ItemGroup>
- <ItemGroup>
- <None Include="ClassDiagram.cd" />
- <None Include="OAuth\Messages\OAuth Messages.cd" />
- <None Include="Messaging\Bindings\Bindings.cd" />
- <None Include="Messaging\Exceptions.cd" />
- <None Include="Messaging\Messaging.cd" />
- </ItemGroup>
- <ItemGroup>
- <EmbeddedResource Include="Messaging\MessagingStrings.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>MessagingStrings.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Include="OAuth\OAuthStrings.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>OAuthStrings.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Include="OpenId\OpenIdStrings.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>OpenIdStrings.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- <EmbeddedResource Include="Strings.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>Strings.Designer.cs</LastGenOutput>
- <SubType>Designer</SubType>
- </EmbeddedResource>
- <EmbeddedResource Include="Xrds\XrdsStrings.resx">
- <Generator>ResXFileCodeGenerator</Generator>
- <LastGenOutput>XrdsStrings.Designer.cs</LastGenOutput>
- </EmbeddedResource>
- </ItemGroup>
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
- <Import Project="..\..\tools\DotNetOpenAuth.Versioning.targets" />
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.30729</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>DotNetOpenAuth</RootNamespace>
+ <AssemblyName>DotNetOpenAuth</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>..\..\bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
+ <DocumentationFile>..\..\bin\Debug\DotNetOpenAuth.xml</DocumentationFile>
+ <RunCodeAnalysis>false</RunCodeAnalysis>
+ <CodeAnalysisRules>-Microsoft.Design#CA1054;-Microsoft.Design#CA1056;-Microsoft.Design#CA1055</CodeAnalysisRules>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>..\..\bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
+ <DocumentationFile>..\..\bin\debug\DotNetOpenAuth.xml</DocumentationFile>
+ <RunCodeAnalysis>true</RunCodeAnalysis>
+ <CodeAnalysisRules>-Microsoft.Design#CA1054;-Microsoft.Design#CA1056;-Microsoft.Design#CA1055</CodeAnalysisRules>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Sign)' == 'true' ">
+ <SignAssembly>true</SignAssembly>
+ <AssemblyOriginatorKeyFile>..\official-build-key.pfx</AssemblyOriginatorKeyFile>
+ <DefineConstants>$(DefineConstants);StrongNameSigned</DefineConstants>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\lib\log4net.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.configuration" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.ServiceModel">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Web" />
+ <Reference Include="System.XML" />
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Configuration\ProviderSection.cs" />
+ <Compile Include="Configuration\ProviderSecuritySettingsElement.cs" />
+ <Compile Include="Configuration\RelyingPartySection.cs" />
+ <Compile Include="Configuration\RelyingPartySecuritySettingsElement.cs" />
+ <Compile Include="Configuration\TypeConfigurationElement.cs" />
+ <Compile Include="Configuration\UntrustedWebRequestSection.cs" />
+ <Compile Include="Configuration\HostNameOrRegexCollection.cs" />
+ <Compile Include="Configuration\HostNameElement.cs" />
+ <Compile Include="Messaging\IExtensionMessage.cs" />
+ <Compile Include="Messaging\IMessage.cs" />
+ <Compile Include="Messaging\DirectWebResponse.cs" />
+ <Compile Include="Messaging\IDirectResponseProtocolMessage.cs" />
+ <Compile Include="Messaging\EmptyDictionary.cs" />
+ <Compile Include="Messaging\EmptyEnumerator.cs" />
+ <Compile Include="Messaging\EmptyList.cs" />
+ <Compile Include="Messaging\ErrorUtilities.cs" />
+ <Compile Include="Messaging\IDirectSslWebRequestHandler.cs" />
+ <Compile Include="Messaging\IProtocolMessageWithExtensions.cs" />
+ <Compile Include="Messaging\InternalErrorException.cs" />
+ <Compile Include="Messaging\Reflection\IMessagePartEncoder.cs" />
+ <Compile Include="OAuth\ChannelElements\OAuthConsumerMessageFactory.cs" />
+ <Compile Include="OAuth\ChannelElements\ITokenGenerator.cs" />
+ <Compile Include="OAuth\ChannelElements\ITokenManager.cs" />
+ <Compile Include="OAuth\ChannelElements\OAuthHttpMethodBindingElement.cs" />
+ <Compile Include="OAuth\ChannelElements\PlaintextSigningBindingElement.cs" />
+ <Compile Include="OAuth\ChannelElements\HmacSha1SigningBindingElement.cs" />
+ <Compile Include="OAuth\ChannelElements\SigningBindingElementChain.cs" />
+ <Compile Include="OAuth\ChannelElements\StandardTokenGenerator.cs" />
+ <Compile Include="OAuth\ChannelElements\TokenType.cs" />
+ <Compile Include="OAuth\ConsumerBase.cs" />
+ <Compile Include="OAuth\DesktopConsumer.cs" />
+ <Compile Include="GlobalSuppressions.cs" />
+ <Compile Include="OAuth\Messages\ITokenSecretContainingMessage.cs" />
+ <Compile Include="Messaging\ChannelEventArgs.cs" />
+ <Compile Include="Messaging\ITamperProtectionChannelBindingElement.cs" />
+ <Compile Include="OAuth\OAuthStrings.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>OAuthStrings.resx</DependentUpon>
+ </Compile>
+ <Compile Include="OAuth\ServiceProviderDescription.cs" />
+ <Compile Include="OAuth\Messages\ITokenContainingMessage.cs" />
+ <Compile Include="OAuth\Messages\SignedMessageBase.cs" />
+ <Compile Include="Messaging\Bindings\NonceMemoryStore.cs" />
+ <Compile Include="OAuth\ChannelElements\SigningBindingElementBase.cs" />
+ <Compile Include="OAuth\WebConsumer.cs" />
+ <Compile Include="Messaging\IDirectWebRequestHandler.cs" />
+ <Compile Include="OAuth\ChannelElements\ITamperResistantOAuthMessage.cs" />
+ <Compile Include="OAuth\Messages\MessageBase.cs" />
+ <Compile Include="OAuth\Messages\AuthorizedTokenRequest.cs" />
+ <Compile Include="Messaging\Bindings\INonceStore.cs" />
+ <Compile Include="Messaging\Bindings\StandardReplayProtectionBindingElement.cs" />
+ <Compile Include="Messaging\MessagePartAttribute.cs" />
+ <Compile Include="Messaging\MessageProtections.cs" />
+ <Compile Include="Messaging\IChannelBindingElement.cs" />
+ <Compile Include="Messaging\Bindings\ReplayedMessageException.cs" />
+ <Compile Include="Messaging\Bindings\ExpiredMessageException.cs" />
+ <Compile Include="Messaging\Bindings\InvalidSignatureException.cs" />
+ <Compile Include="Messaging\Bindings\IReplayProtectedProtocolMessage.cs" />
+ <Compile Include="Messaging\Bindings\IExpiringProtocolMessage.cs" />
+ <Compile Include="OAuth\Messages\AccessProtectedResourceRequest.cs" />
+ <Compile Include="OAuth\Messages\AuthorizedTokenResponse.cs" />
+ <Compile Include="OAuth\Messages\UserAuthorizationResponse.cs" />
+ <Compile Include="OAuth\Messages\UserAuthorizationRequest.cs" />
+ <Compile Include="OAuth\Messages\UnauthorizedTokenResponse.cs" />
+ <Compile Include="Messaging\Channel.cs" />
+ <Compile Include="Messaging\HttpRequestInfo.cs" />
+ <Compile Include="Messaging\IDirectedProtocolMessage.cs" />
+ <Compile Include="Messaging\IMessageFactory.cs" />
+ <Compile Include="Messaging\ITamperResistantProtocolMessage.cs" />
+ <Compile Include="Messaging\MessageSerializer.cs" />
+ <Compile Include="Messaging\MessagingStrings.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>MessagingStrings.resx</DependentUpon>
+ </Compile>
+ <Compile Include="Messaging\MessagingUtilities.cs" />
+ <Compile Include="Messaging\Bindings\StandardExpirationBindingElement.cs" />
+ <Compile Include="Messaging\Reflection\ValueMapping.cs" />
+ <Compile Include="Messaging\Reflection\MessageDescription.cs" />
+ <Compile Include="Messaging\Reflection\MessageDictionary.cs" />
+ <Compile Include="Messaging\Reflection\MessagePart.cs" />
+ <Compile Include="Messaging\UnprotectedMessageException.cs" />
+ <Compile Include="OAuth\ChannelElements\OAuthChannel.cs" />
+ <Compile Include="Messaging\UserAgentResponse.cs" />
+ <Compile Include="Messaging\IProtocolMessage.cs" />
+ <Compile Include="Logger.cs" />
+ <Compile Include="Loggers\ILog.cs" />
+ <Compile Include="Loggers\Log4NetLogger.cs" />
+ <Compile Include="Loggers\NoOpLogger.cs" />
+ <Compile Include="Loggers\TraceLogger.cs" />
+ <Compile Include="Messaging\HttpDeliveryMethods.cs" />
+ <Compile Include="Messaging\MessageTransport.cs" />
+ <Compile Include="OAuth\ChannelElements\OAuthServiceProviderMessageFactory.cs" />
+ <Compile Include="Messaging\ProtocolException.cs" />
+ <Compile Include="OpenId\AliasManager.cs" />
+ <Compile Include="OpenId\Association.cs" />
+ <Compile Include="OpenId\AssociationMemoryStore.cs" />
+ <Compile Include="OpenId\Associations.cs" />
+ <Compile Include="OpenId\ChannelElements\ExtensionsBindingElement.cs" />
+ <Compile Include="OpenId\ChannelElements\ITamperResistantOpenIdMessage.cs" />
+ <Compile Include="OpenId\ChannelElements\OriginalStringUriEncoder.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\Configuration.cs" />
+ <Compile Include="OpenId\Extensions\AliasManager.cs" />
+ <Compile Include="OpenId\Extensions\ExtensionBase.cs" />
+ <Compile Include="OpenId\Extensions\IOutgoingExtensions.cs" />
+ <Compile Include="OpenId\Extensions\ExtensionArgumentsManager.cs" />
+ <None Include="OpenId\Extensions\IExtension.cs" />
+ <Compile Include="OpenId\Extensions\IIncomingExtensions.cs" />
+ <Compile Include="OpenId\Extensions\SimpleRegistration\ClaimsRequest.cs" />
+ <Compile Include="OpenId\Extensions\SimpleRegistration\ClaimsResponse.cs" />
+ <Compile Include="OpenId\Extensions\SimpleRegistration\Constants.cs" />
+ <Compile Include="OpenId\Extensions\SimpleRegistration\DemandLevel.cs" />
+ <Compile Include="OpenId\Extensions\SimpleRegistration\Gender.cs" />
+ <Compile Include="OpenId\Identifier.cs" />
+ <Compile Include="OpenId\Messages\CheckAuthenticationRequest.cs" />
+ <Compile Include="OpenId\Messages\CheckAuthenticationResponse.cs" />
+ <Compile Include="OpenId\Messages\CheckIdRequest.cs" />
+ <Compile Include="OpenId\Messages\IndirectResponseBase.cs" />
+ <Compile Include="OpenId\Messages\IndirectSignedResponse.cs" />
+ <Compile Include="OpenId\Messages\IOpenIdProtocolMessageExtension.cs" />
+ <Compile Include="OpenId\Messages\NegativeAssertionResponse.cs" />
+ <Compile Include="OpenId\Messages\PositiveAssertionResponse.cs" />
+ <Compile Include="OpenId\Messages\SignedResponseRequest.cs" />
+ <Compile Include="OpenId\NoDiscoveryIdentifier.cs" />
+ <Compile Include="OpenId\OpenIdUtilities.cs" />
+ <Compile Include="OpenId\Realm.cs" />
+ <Compile Include="OpenId\RelyingPartyDescription.cs" />
+ <Compile Include="OpenId\DiffieHellmanUtilities.cs" />
+ <Compile Include="OpenId\DiffieHellman\DHKeyGeneration.cs" />
+ <Compile Include="OpenId\DiffieHellman\DHParameters.cs" />
+ <Compile Include="OpenId\DiffieHellman\DiffieHellman.cs" />
+ <Compile Include="OpenId\DiffieHellman\DiffieHellmanManaged.cs" />
+ <Compile Include="OpenId\DiffieHellman\mono\BigInteger.cs" />
+ <Compile Include="OpenId\DiffieHellman\mono\ConfidenceFactor.cs" />
+ <Compile Include="OpenId\DiffieHellman\mono\NextPrimeFinder.cs" />
+ <Compile Include="OpenId\DiffieHellman\mono\PrimalityTests.cs" />
+ <Compile Include="OpenId\DiffieHellman\mono\PrimeGeneratorBase.cs" />
+ <Compile Include="OpenId\DiffieHellman\mono\SequentialSearchPrimeGeneratorBase.cs" />
+ <Compile Include="OpenId\HmacShaAssociation.cs" />
+ <Compile Include="OpenId\IAssociationStore.cs" />
+ <Compile Include="OpenId\Messages\AssociateUnencryptedRequest.cs" />
+ <Compile Include="OpenId\Provider\OpenIdProvider.cs" />
+ <Compile Include="OpenId\Messages\AssociateDiffieHellmanRequest.cs" />
+ <Compile Include="OpenId\Messages\AssociateDiffieHellmanResponse.cs" />
+ <Compile Include="OpenId\Messages\AssociateRequest.cs" />
+ <Compile Include="OpenId\Messages\AssociateSuccessfulResponse.cs" />
+ <Compile Include="OpenId\Messages\AssociateUnencryptedResponse.cs" />
+ <Compile Include="OpenId\Messages\AssociateUnsuccessfulResponse.cs" />
+ <Compile Include="OpenId\Messages\IndirectErrorResponse.cs" />
+ <Compile Include="OpenId\Messages\DirectErrorResponse.cs" />
+ <Compile Include="OpenId\Messages\RequestBase.cs" />
+ <Compile Include="OpenId\Messages\DirectResponseBase.cs" />
+ <Compile Include="OpenId\RelyingParty\IProviderEndpoint.cs" />
+ <Compile Include="OpenId\RelyingParty\IXrdsProviderEndpoint.cs" />
+ <Compile Include="OpenId\RelyingParty\OpenIdRelyingParty.cs" />
+ <Compile Include="OpenId\OpenIdStrings.Designer.cs">
+ <DependentUpon>OpenIdStrings.resx</DependentUpon>
+ <DesignTime>True</DesignTime>
+ <AutoGen>True</AutoGen>
+ </Compile>
+ <Compile Include="OpenId\Protocol.cs" />
+ <Compile Include="OpenId\ProviderDescription.cs" />
+ <Compile Include="OpenId\Provider\ProviderSecuritySettings.cs" />
+ <Compile Include="OpenId\RelyingParty\RelyingPartySecuritySettings.cs" />
+ <Compile Include="OpenId\RelyingParty\ServiceEndpoint.cs" />
+ <Compile Include="OpenId\OpenIdXrdsHelper.cs" />
+ <Compile Include="OpenId\SecuritySettings.cs" />
+ <Compile Include="Messaging\UntrustedWebRequestHandler.cs" />
+ <Compile Include="OpenId\UriIdentifier.cs" />
+ <Compile Include="OpenId\XriIdentifier.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="OAuth\Messages\UnauthorizedTokenRequest.cs" />
+ <Compile Include="OAuth\ChannelElements\RsaSha1SigningBindingElement.cs" />
+ <Compile Include="Messaging\StandardWebRequestHandler.cs" />
+ <Compile Include="Messaging\MessageReceivingEndpoint.cs" />
+ <Compile Include="Util.cs" />
+ <Compile Include="OAuth\Protocol.cs" />
+ <Compile Include="OAuth\ServiceProvider.cs" />
+ <Compile Include="Strings.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>Strings.resx</DependentUpon>
+ </Compile>
+ <Compile Include="UriUtil.cs" />
+ <Compile Include="Xrds\XrdsStrings.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>XrdsStrings.resx</DependentUpon>
+ </Compile>
+ <Compile Include="Yadis\ContentTypes.cs" />
+ <Compile Include="Yadis\DiscoveryResult.cs" />
+ <Compile Include="Yadis\HtmlParser.cs" />
+ <Compile Include="Xrds\ServiceElement.cs" />
+ <Compile Include="Xrds\TypeElement.cs" />
+ <Compile Include="Xrds\UriElement.cs" />
+ <Compile Include="Xrds\XrdElement.cs" />
+ <Compile Include="Xrds\XrdsDocument.cs" />
+ <Compile Include="Xrds\XrdsNode.cs" />
+ <Compile Include="Yadis\Yadis.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="ClassDiagram.cd" />
+ <None Include="OAuth\Messages\OAuth Messages.cd" />
+ <None Include="Messaging\Bindings\Bindings.cd" />
+ <None Include="Messaging\Exceptions.cd" />
+ <None Include="Messaging\Messaging.cd" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Messaging\MessagingStrings.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>MessagingStrings.Designer.cs</LastGenOutput>
+ </EmbeddedResource>
+ <EmbeddedResource Include="OAuth\OAuthStrings.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>OAuthStrings.Designer.cs</LastGenOutput>
+ </EmbeddedResource>
+ <EmbeddedResource Include="OpenId\OpenIdStrings.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>OpenIdStrings.Designer.cs</LastGenOutput>
+ </EmbeddedResource>
+ <EmbeddedResource Include="Strings.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Strings.Designer.cs</LastGenOutput>
+ <SubType>Designer</SubType>
+ </EmbeddedResource>
+ <EmbeddedResource Include="Xrds\XrdsStrings.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>XrdsStrings.Designer.cs</LastGenOutput>
+ </EmbeddedResource>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <Import Project="..\..\tools\DotNetOpenAuth.Versioning.targets" />
</Project> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/Messaging/Channel.cs b/src/DotNetOpenAuth/Messaging/Channel.cs
index 46b932d..9ac65f8 100644
--- a/src/DotNetOpenAuth/Messaging/Channel.cs
+++ b/src/DotNetOpenAuth/Messaging/Channel.cs
@@ -1,782 +1,782 @@
-//-----------------------------------------------------------------------
-// <copyright file="Channel.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Diagnostics;
- using System.Diagnostics.CodeAnalysis;
- using System.Globalization;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Text;
- using System.Web;
- using DotNetOpenAuth.Messaging.Reflection;
-
- /// <summary>
- /// Manages sending direct messages to a remote party and receiving responses.
- /// </summary>
- public abstract class Channel {
- /// <summary>
- /// The maximum allowable size for a 301 Redirect response before we send
- /// a 200 OK response with a scripted form POST with the parameters instead
- /// in order to ensure successfully sending a large payload to another server
- /// that might have a maximum allowable size restriction on its GET request.
- /// </summary>
- private static int indirectMessageGetToPostThreshold = 2 * 1024; // 2KB, recommended by OpenID group
-
- /// <summary>
- /// The template for indirect messages that require form POST to forward through the user agent.
- /// </summary>
- /// <remarks>
- /// We are intentionally using " instead of the html single quote ' below because
- /// the HtmlEncode'd values that we inject will only escape the double quote, so
- /// only the double-quote used around these values is safe.
- /// </remarks>
- private static string indirectMessageFormPostFormat = @"
-<html>
-<body onload=""var btn = document.getElementById('submit_button'); btn.disabled = true; btn.value = 'Login in progress'; document.getElementById('openid_message').submit()"">
-<form id=""openid_message"" action=""{0}"" method=""post"" accept-charset=""UTF-8"" enctype=""application/x-www-form-urlencoded"" onSubmit=""var btn = document.getElementById('submit_button'); btn.disabled = true; btn.value = 'Login in progress'; return true;"">
-{1}
- <input id=""submit_button"" type=""submit"" value=""Continue"" />
-</form>
-</body>
-</html>
-";
-
- /// <summary>
- /// A tool that can figure out what kind of message is being received
- /// so it can be deserialized.
- /// </summary>
- private IMessageFactory messageTypeProvider;
-
- /// <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>();
-
- /// <summary>
- /// Initializes a new instance of the <see cref="Channel"/> class.
- /// </summary>
- /// <param name="messageTypeProvider">
- /// A class prepared to analyze incoming messages and indicate what concrete
- /// message types can deserialize from it.
- /// </param>
- /// <param name="bindingElements">The binding elements to use in sending and receiving messages.</param>
- protected Channel(IMessageFactory messageTypeProvider, params IChannelBindingElement[] bindingElements) {
- if (messageTypeProvider == null) {
- throw new ArgumentNullException("messageTypeProvider");
- }
-
- this.messageTypeProvider = messageTypeProvider;
- this.WebRequestHandler = new StandardWebRequestHandler();
- this.bindingElements = new List<IChannelBindingElement>(ValidateAndPrepareBindingElements(bindingElements));
-
- foreach (var element in this.bindingElements) {
- element.Channel = this;
- }
- }
-
- /// <summary>
- /// An event fired whenever a message is about to be encoded and sent.
- /// </summary>
- internal event EventHandler<ChannelEventArgs> Sending;
-
- /// <summary>
- /// Gets or sets an instance to a <see cref="IDirectWebRequestHandler"/> that will be used when
- /// submitting HTTP requests and waiting for responses.
- /// </summary>
- /// <remarks>
- /// This defaults to a straightforward implementation, but can be set
- /// to a mock object for testing purposes.
- /// </remarks>
- public IDirectWebRequestHandler WebRequestHandler { get; set; }
-
- /// <summary>
- /// Gets the binding elements used by this channel, in the order they are applied to outgoing messages.
- /// </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();
- }
- }
-
- /// <summary>
- /// Gets a tool that can figure out what kind of message is being received
- /// so it can be deserialized.
- /// </summary>
- protected IMessageFactory MessageFactory {
- get { return this.messageTypeProvider; }
- }
-
- /// <summary>
- /// Queues an indirect message (either a request or response)
- /// or direct message response for transmission to a remote party.
- /// </summary>
- /// <param name="message">The one-way message to send</param>
- /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
- public UserAgentResponse Send(IProtocolMessage message) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
- this.PrepareMessageForSending(message);
- Logger.DebugFormat("Sending message: {0}", message);
-
- switch (message.Transport) {
- case MessageTransport.Direct:
- // This is a response to a direct message.
- return this.SendDirectMessageResponse(message);
- case MessageTransport.Indirect:
- var directedMessage = message as IDirectedProtocolMessage;
- if (directedMessage == null) {
- throw new ArgumentException(
- string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.IndirectMessagesMustImplementIDirectedProtocolMessage,
- typeof(IDirectedProtocolMessage).FullName),
- "message");
- }
- if (directedMessage.Recipient == null) {
- throw new ArgumentException(MessagingStrings.DirectedMessageMissingRecipient, "message");
- }
- return this.SendIndirectMessage(directedMessage);
- default:
- throw new ArgumentException(
- string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.UnrecognizedEnumValue,
- "Transport",
- message.Transport),
- "message");
- }
- }
-
- /// <summary>
- /// Gets the protocol message embedded in the given HTTP request, if present.
- /// </summary>
- /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
- /// <remarks>
- /// Requires an HttpContext.Current context.
- /// </remarks>
- /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
- public IDirectedProtocolMessage ReadFromRequest() {
- return this.ReadFromRequest(this.GetRequestFromContext());
- }
-
- /// <summary>
- /// Gets the protocol message embedded in the given HTTP request, if present.
- /// </summary>
- /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
- /// <param name="request">The deserialized message, if one is found. Null otherwise.</param>
- /// <returns>True if the expected message was recognized and deserialized. False otherwise.</returns>
- /// <remarks>
- /// Requires an HttpContext.Current context.
- /// </remarks>
- /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
- /// <exception cref="ProtocolException">Thrown when a request message of an unexpected type is received.</exception>
- public bool TryReadFromRequest<TRequest>(out TRequest request)
- where TRequest : class, IProtocolMessage {
- return TryReadFromRequest<TRequest>(this.GetRequestFromContext(), out request);
- }
-
- /// <summary>
- /// Gets the protocol message embedded in the given HTTP request, if present.
- /// </summary>
- /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
- /// <param name="httpRequest">The request to search for an embedded message.</param>
- /// <param name="request">The deserialized message, if one is found. Null otherwise.</param>
- /// <returns>True if the expected message was recognized and deserialized. False otherwise.</returns>
- /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
- /// <exception cref="ProtocolException">Thrown when a request message of an unexpected type is received.</exception>
- public bool TryReadFromRequest<TRequest>(HttpRequestInfo httpRequest, out TRequest request)
- where TRequest : class, IProtocolMessage {
- IProtocolMessage untypedRequest = this.ReadFromRequest(httpRequest);
- if (untypedRequest == null) {
- request = null;
- return false;
- }
-
- request = untypedRequest as TRequest;
- if (request == null) {
- throw new ProtocolException(
- string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.UnexpectedMessageReceived,
- typeof(TRequest),
- untypedRequest.GetType()));
- }
-
- return true;
- }
-
- /// <summary>
- /// Gets the protocol message embedded in the given HTTP request, if present.
- /// </summary>
- /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
- /// <returns>The deserialized message.</returns>
- /// <remarks>
- /// Requires an HttpContext.Current context.
- /// </remarks>
- /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
- /// <exception cref="ProtocolException">Thrown if the expected message was not recognized in the response.</exception>
- [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")]
- public TRequest ReadFromRequest<TRequest>()
- where TRequest : class, IProtocolMessage {
- return this.ReadFromRequest<TRequest>(this.GetRequestFromContext());
- }
-
- /// <summary>
- /// Gets the protocol message that may be embedded in the given HTTP request.
- /// </summary>
- /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
- /// <param name="httpRequest">The request to search for an embedded message.</param>
- /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
- /// <exception cref="ProtocolException">Thrown if the expected message was not recognized in the response.</exception>
- [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")]
- public TRequest ReadFromRequest<TRequest>(HttpRequestInfo httpRequest)
- where TRequest : class, IProtocolMessage {
- TRequest request;
- if (this.TryReadFromRequest<TRequest>(httpRequest, out request)) {
- return request;
- } else {
- throw new ProtocolException(
- string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.ExpectedMessageNotReceived,
- typeof(TRequest)));
- }
- }
-
- /// <summary>
- /// Gets the protocol message that may be embedded in the given HTTP request.
- /// </summary>
- /// <param name="httpRequest">The request to search for an embedded message.</param>
- /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
- public IDirectedProtocolMessage ReadFromRequest(HttpRequestInfo httpRequest) {
- IDirectedProtocolMessage requestMessage = this.ReadFromRequestInternal(httpRequest);
- if (requestMessage != null) {
- Logger.DebugFormat("Incoming request received: {0}", requestMessage);
- this.VerifyMessageAfterReceiving(requestMessage);
- }
-
- return requestMessage;
- }
-
- /// <summary>
- /// Sends a direct message to a remote party and waits for the response.
- /// </summary>
- /// <typeparam name="TResponse">The expected type of the message to be received.</typeparam>
- /// <param name="requestMessage">The message to send.</param>
- /// <returns>The remote party's response.</returns>
- /// <exception cref="ProtocolException">
- /// Thrown if no message is recognized in the response
- /// or an unexpected type of message is received.
- /// </exception>
- [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")]
- public TResponse Request<TResponse>(IDirectedProtocolMessage requestMessage)
- where TResponse : class, IProtocolMessage {
- IProtocolMessage response = this.Request(requestMessage);
- ErrorUtilities.VerifyProtocol(response != null, MessagingStrings.ExpectedMessageNotReceived, typeof(TResponse));
-
- var expectedResponse = response as TResponse;
- ErrorUtilities.VerifyProtocol(expectedResponse != null, MessagingStrings.UnexpectedMessageReceived, typeof(TResponse), response.GetType());
-
- return expectedResponse;
- }
-
- /// <summary>
- /// Sends a direct message to a remote party and waits for the response.
- /// </summary>
- /// <param name="requestMessage">The message to send.</param>
- /// <returns>The remote party's response. Guaranteed to never be null.</returns>
- /// <exception cref="ProtocolException">Thrown if the response does not include a protocol message.</exception>
- public IProtocolMessage Request(IDirectedProtocolMessage requestMessage) {
- if (requestMessage == null) {
- throw new ArgumentNullException("requestMessage");
- }
-
- this.PrepareMessageForSending(requestMessage);
- Logger.DebugFormat("Sending request: {0}", requestMessage);
- var responseMessage = this.RequestInternal(requestMessage);
- ErrorUtilities.VerifyProtocol(responseMessage != null, MessagingStrings.ExpectedMessageNotReceived, typeof(IProtocolMessage).Name);
-
- Logger.DebugFormat("Received message response: {0}", responseMessage);
- this.VerifyMessageAfterReceiving(responseMessage);
-
- return responseMessage;
- }
-
- /// <summary>
- /// Gets the current HTTP request being processed.
- /// </summary>
- /// <returns>The HttpRequestInfo for the current request.</returns>
- /// <remarks>
- /// Requires an HttpContext.Current context.
- /// </remarks>
- /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
- [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Costly call should not be a property.")]
- protected internal virtual HttpRequestInfo GetRequestFromContext() {
- if (HttpContext.Current == null) {
- throw new InvalidOperationException(MessagingStrings.HttpContextRequired);
- }
-
- return new HttpRequestInfo(HttpContext.Current.Request);
- }
-
- /// <summary>
- /// Fires the <see cref="Sending"/> event.
- /// </summary>
- /// <param name="message">The message about to be encoded and sent.</param>
- protected virtual void OnSending(IProtocolMessage message) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
-
- var sending = this.Sending;
- if (sending != null) {
- sending(this, new ChannelEventArgs(message));
- }
- }
-
- /// <summary>
- /// Submits a direct request message to some remote party and blocks waiting for an immediately reply.
- /// </summary>
- /// <param name="request">The request message.</param>
- /// <returns>The response message, or null if the response did not carry a message.</returns>
- /// <remarks>
- /// Typically a deriving channel will override <see cref="CreateHttpRequest"/> to customize this method's
- /// behavior. However in non-HTTP frameworks, such as unit test mocks, it may be appropriate to override
- /// this method to eliminate all use of an HTTP transport.
- /// </remarks>
- protected virtual IProtocolMessage RequestInternal(IDirectedProtocolMessage request) {
- HttpWebRequest webRequest = this.CreateHttpRequest(request);
- IDictionary<string, string> responseFields;
-
- using (DirectWebResponse response = this.WebRequestHandler.GetResponse(webRequest)) {
- if (response.ResponseStream == null) {
- return null;
- }
-
- responseFields = this.ReadFromResponseInternal(response);
- }
-
- IDirectResponseProtocolMessage responseMessage = this.MessageFactory.GetNewResponseMessage(request, responseFields);
- if (responseMessage == null) {
- return null;
- }
-
- var responseSerialize = MessageSerializer.Get(responseMessage.GetType());
- responseSerialize.Deserialize(responseFields, responseMessage);
-
- return responseMessage;
- }
-
- /// <summary>
- /// Gets the protocol message that may be embedded in the given HTTP request.
- /// </summary>
- /// <param name="request">The request to search for an embedded message.</param>
- /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
- protected virtual IDirectedProtocolMessage ReadFromRequestInternal(HttpRequestInfo request) {
- if (request == null) {
- throw new ArgumentNullException("request");
- }
-
- // Search Form data first, and if nothing is there search the QueryString
- var fields = request.Form.ToDictionary();
- if (fields.Count == 0) {
- fields = request.QueryString.ToDictionary();
- }
-
- return (IDirectedProtocolMessage)this.Receive(fields, request.GetRecipient());
- }
-
- /// <summary>
- /// Deserializes a dictionary of values into a message.
- /// </summary>
- /// <param name="fields">The dictionary of values that were read from an HTTP request or response.</param>
- /// <param name="recipient">Information about where the message was been directed. Null for direct response messages.</param>
- /// <returns>The deserialized message, or null if no message could be recognized in the provided data.</returns>
- protected virtual IProtocolMessage Receive(Dictionary<string, string> fields, MessageReceivingEndpoint recipient) {
- if (fields == null) {
- throw new ArgumentNullException("fields");
- }
-
- IProtocolMessage message = this.MessageFactory.GetNewRequestMessage(recipient, fields);
-
- // If there was no data, or we couldn't recognize it as a message, abort.
- if (message == null) {
- return null;
- }
-
- // We have a message! Assemble it.
- var serializer = MessageSerializer.Get(message.GetType());
- serializer.Deserialize(fields, message);
-
- return message;
- }
-
- /// <summary>
- /// Queues an indirect message for transmittal via the user agent.
- /// </summary>
- /// <param name="message">The message to send.</param>
- /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
- protected virtual UserAgentResponse SendIndirectMessage(IDirectedProtocolMessage message) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
-
- var serializer = MessageSerializer.Get(message.GetType());
- var fields = serializer.Serialize(message);
- UserAgentResponse response;
- if (CalculateSizeOfPayload(fields) > indirectMessageGetToPostThreshold) {
- response = this.CreateFormPostResponse(message, fields);
- } else {
- response = this.Create301RedirectResponse(message, fields);
- }
-
- return response;
- }
-
- /// <summary>
- /// Encodes an HTTP response that will instruct the user agent to forward a message to
- /// some remote third party using a 301 Redirect GET method.
- /// </summary>
- /// <param name="message">The message to forward.</param>
- /// <param name="fields">The pre-serialized fields from the message.</param>
- /// <returns>The encoded HTTP response.</returns>
- protected virtual UserAgentResponse Create301RedirectResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
- if (message.Recipient == null) {
- throw new ArgumentException(MessagingStrings.DirectedMessageMissingRecipient, "message");
- }
- if (fields == null) {
- throw new ArgumentNullException("fields");
- }
-
- WebHeaderCollection headers = new WebHeaderCollection();
- UriBuilder builder = new UriBuilder(message.Recipient);
- MessagingUtilities.AppendQueryArgs(builder, fields);
- headers.Add(HttpResponseHeader.Location, builder.Uri.AbsoluteUri);
- Logger.DebugFormat("Redirecting to {0}", builder.Uri.AbsoluteUri);
- UserAgentResponse response = new UserAgentResponse {
- Status = HttpStatusCode.Redirect,
- Headers = headers,
- Body = null,
- OriginalMessage = message
- };
-
- return response;
- }
-
- /// <summary>
- /// Encodes an HTTP response that will instruct the user agent to forward a message to
- /// some remote third party using a form POST method.
- /// </summary>
- /// <param name="message">The message to forward.</param>
- /// <param name="fields">The pre-serialized fields from the message.</param>
- /// <returns>The encoded HTTP response.</returns>
- protected virtual UserAgentResponse CreateFormPostResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
- if (message.Recipient == null) {
- throw new ArgumentException(MessagingStrings.DirectedMessageMissingRecipient, "message");
- }
- if (fields == null) {
- throw new ArgumentNullException("fields");
- }
-
- WebHeaderCollection headers = new WebHeaderCollection();
- StringWriter bodyWriter = new StringWriter(CultureInfo.InvariantCulture);
- StringBuilder hiddenFields = new StringBuilder();
- foreach (var field in fields) {
- hiddenFields.AppendFormat(
- "\t<input type=\"hidden\" name=\"{0}\" value=\"{1}\" />\r\n",
- HttpUtility.HtmlEncode(field.Key),
- HttpUtility.HtmlEncode(field.Value));
- }
- bodyWriter.WriteLine(
- indirectMessageFormPostFormat,
- HttpUtility.HtmlEncode(message.Recipient.AbsoluteUri),
- hiddenFields);
- bodyWriter.Flush();
- UserAgentResponse response = new UserAgentResponse {
- Status = HttpStatusCode.OK,
- Headers = headers,
- Body = bodyWriter.ToString(),
- OriginalMessage = message
- };
-
- return response;
- }
-
- /// <summary>
- /// Gets the protocol message that may be in the given HTTP response.
- /// </summary>
- /// <param name="response">The response that is anticipated to contain an protocol message.</param>
- /// <returns>The deserialized message parts, if found. Null otherwise.</returns>
- protected abstract IDictionary<string, string> ReadFromResponseInternal(DirectWebResponse response);
-
- /// <summary>
- /// Prepares an HTTP request that carries a given message.
- /// </summary>
- /// <param name="request">The message to send.</param>
- /// <returns>The <see cref="HttpWebRequest"/> prepared to send the request.</returns>
- /// <remarks>
- /// This method must be overridden by a derived class, unless the <see cref="RequestInternal"/> method
- /// is overridden and does not require this method.
- /// </remarks>
- protected virtual HttpWebRequest CreateHttpRequest(IDirectedProtocolMessage request) {
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Queues a message for sending in the response stream where the fields
- /// are sent in the response stream in querystring style.
- /// </summary>
- /// <param name="response">The message to send as a response.</param>
- /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
- /// <remarks>
- /// This method implements spec V1.0 section 5.3.
- /// </remarks>
- protected abstract UserAgentResponse SendDirectMessageResponse(IProtocolMessage response);
-
- /// <summary>
- /// Prepares a message for transmit by applying signatures, nonces, etc.
- /// </summary>
- /// <param name="message">The message to prepare for sending.</param>
- /// <remarks>
- /// This method should NOT be called by derived types
- /// except when sending ONE WAY request messages.
- /// </remarks>
- protected void PrepareMessageForSending(IProtocolMessage message) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
-
- this.OnSending(message);
-
- MessageProtections appliedProtection = MessageProtections.None;
- foreach (IChannelBindingElement bindingElement in this.bindingElements) {
- if (bindingElement.PrepareMessageForSending(message)) {
- appliedProtection |= bindingElement.Protection;
- }
- }
-
- // Ensure that the message's protection requirements have been satisfied.
- if ((message.RequiredProtection & appliedProtection) != message.RequiredProtection) {
- throw new UnprotectedMessageException(message, appliedProtection);
- }
-
- EnsureValidMessageParts(message);
- message.EnsureValidMessage();
- }
-
- /// <summary>
- /// Prepares to send a request to the Service Provider as the query string in a GET request.
- /// </summary>
- /// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
- /// <returns>The web request ready to send.</returns>
- /// <remarks>
- /// This method is simply a standard HTTP Get request with the message parts serialized to the query string.
- /// This method satisfies OAuth 1.0 section 5.2, item #3.
- /// </remarks>
- protected virtual HttpWebRequest InitializeRequestAsGet(IDirectedProtocolMessage requestMessage) {
- if (requestMessage == null) {
- throw new ArgumentNullException("requestMessage");
- }
-
- var serializer = MessageSerializer.Get(requestMessage.GetType());
- var fields = serializer.Serialize(requestMessage);
-
- UriBuilder builder = new UriBuilder(requestMessage.Recipient);
- MessagingUtilities.AppendQueryArgs(builder, fields);
- HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(builder.Uri);
-
- return httpRequest;
- }
-
- /// <summary>
- /// Prepares to send a request to the Service Provider as the payload of a POST request.
- /// </summary>
- /// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
- /// <returns>The web request ready to send.</returns>
- /// <remarks>
- /// This method is simply a standard HTTP POST request with the message parts serialized to the POST entity
- /// with the application/x-www-form-urlencoded content type
- /// This method satisfies OAuth 1.0 section 5.2, item #2 and OpenID 2.0 section 4.1.2.
- /// </remarks>
- protected virtual HttpWebRequest InitializeRequestAsPost(IDirectedProtocolMessage requestMessage) {
- if (requestMessage == null) {
- throw new ArgumentNullException("requestMessage");
- }
-
- var serializer = MessageSerializer.Get(requestMessage.GetType());
- var fields = serializer.Serialize(requestMessage);
-
- HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(requestMessage.Recipient);
- httpRequest.Method = "POST";
- httpRequest.ContentType = "application/x-www-form-urlencoded";
- string requestBody = MessagingUtilities.CreateQueryString(fields);
- httpRequest.ContentLength = requestBody.Length;
- using (TextWriter writer = this.WebRequestHandler.GetRequestStream(httpRequest)) {
- writer.Write(requestBody);
- }
-
- return httpRequest;
- }
-
- /// <summary>
- /// Verifies the integrity and applicability of an incoming message.
- /// </summary>
- /// <param name="message">The message just received.</param>
- /// <exception cref="ProtocolException">
- /// Thrown when the message is somehow invalid.
- /// This can be due to tampering, replay attack or expiration, among other things.
- /// </exception>
- protected virtual void VerifyMessageAfterReceiving(IProtocolMessage message) {
- Debug.Assert(message != null, "message == null");
-
- MessageProtections appliedProtection = MessageProtections.None;
- foreach (IChannelBindingElement bindingElement in this.bindingElements.Reverse<IChannelBindingElement>()) {
- if (bindingElement.PrepareMessageForReceiving(message)) {
- appliedProtection |= bindingElement.Protection;
- }
- }
-
- // Ensure that the message's protection requirements have been satisfied.
- if ((message.RequiredProtection & appliedProtection) != message.RequiredProtection) {
- throw new UnprotectedMessageException(message, appliedProtection);
- }
-
- // We do NOT verify that all required message parts are present here... the
- // message deserializer did for us. It would be too late to do it here since
- // they might look initialized by the time we have an IProtocolMessage instance.
- message.EnsureValidMessage();
- }
-
- /// <summary>
- /// Verifies that all required message parts are initialized to values
- /// prior to sending the message to a remote party.
- /// </summary>
- /// <param name="message">The message to verify.</param>
- /// <exception cref="ProtocolException">
- /// Thrown when any required message part does not have a value.
- /// </exception>
- private static void EnsureValidMessageParts(IProtocolMessage message) {
- Debug.Assert(message != null, "message == null");
-
- MessageDictionary dictionary = new MessageDictionary(message);
- MessageDescription description = MessageDescription.Get(message.GetType(), message.ProtocolVersion);
- description.EnsureMessagePartsPassBasicValidation(dictionary);
- }
-
- /// <summary>
- /// Calculates a fairly accurate estimation on the size of a message that contains
- /// a given set of fields.
- /// </summary>
- /// <param name="fields">The fields that would be included in a message.</param>
- /// <returns>The size (in bytes) of the message payload.</returns>
- private static int CalculateSizeOfPayload(IDictionary<string, string> fields) {
- Debug.Assert(fields != null, "fields == null");
-
- int size = 0;
- foreach (var field in fields) {
- size += field.Key.Length;
- size += field.Value.Length;
- size += 2; // & and =
- }
- return size;
- }
-
- /// <summary>
- /// Ensures a consistent and secure set of binding elements and
- /// sorts them as necessary for a valid sequence of operations.
- /// </summary>
- /// <param name="elements">The binding elements provided to the channel.</param>
- /// <returns>The properly ordered list of elements.</returns>
- /// <exception cref="ProtocolException">Thrown when the binding elements are incomplete or inconsistent with each other.</exception>
- private static IEnumerable<IChannelBindingElement> ValidateAndPrepareBindingElements(IEnumerable<IChannelBindingElement> elements) {
- if (elements == null) {
- return new IChannelBindingElement[0];
- }
- if (elements.Contains(null)) {
- throw new ArgumentException(MessagingStrings.SequenceContainsNullElement, "elements");
- }
-
- // Filter the elements between the mere transforming ones and the protection ones.
- var transformationElements = new List<IChannelBindingElement>(
- elements.Where(element => element.Protection == MessageProtections.None));
- var protectionElements = new List<IChannelBindingElement>(
- elements.Where(element => element.Protection != MessageProtections.None));
-
- bool wasLastProtectionPresent = true;
- foreach (MessageProtections protectionKind in Enum.GetValues(typeof(MessageProtections))) {
- if (protectionKind == MessageProtections.None) {
- continue;
- }
-
- int countProtectionsOfThisKind = protectionElements.Count(element => (element.Protection & protectionKind) == protectionKind);
-
- // Each protection binding element is backed by the presence of its dependent protection(s).
- if (countProtectionsOfThisKind > 0 && !wasLastProtectionPresent) {
- throw new ProtocolException(
- string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.RequiredProtectionMissing,
- 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;
- }
-
- // Put the binding elements in order so they are correctly applied to outgoing messages.
- // Start with the transforming (non-protecting) binding elements first and preserve their original order.
- var orderedList = new List<IChannelBindingElement>(transformationElements);
-
- // Now sort the protection binding elements among themselves and add them to the list.
- orderedList.AddRange(protectionElements.OrderBy(element => element.Protection, BindingElementOutgoingMessageApplicationOrder));
- return orderedList;
- }
-
- /// <summary>
- /// Puts binding elements in their correct outgoing message processing order.
- /// </summary>
- /// <param name="protection1">The first protection type to compare.</param>
- /// <param name="protection2">The second protection type to compare.</param>
- /// <returns>
- /// -1 if <paramref name="element1"/> should be applied to an outgoing message before <paramref name="element2"/>.
- /// 1 if <paramref name="element2"/> should be applied to an outgoing message before <paramref name="element1"/>.
- /// 0 if it doesn't matter.
- /// </returns>
- private static int BindingElementOutgoingMessageApplicationOrder(MessageProtections protection1, MessageProtections protection2) {
- Debug.Assert(protection1 != MessageProtections.None || protection2 != MessageProtections.None, "This comparison function should only be used to compare protection binding elements. Otherwise we change the order of user-defined message transformations.");
-
- // Now put the protection ones in the right order.
- return -((int)protection1).CompareTo((int)protection2); // descending flag ordinal order
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="Channel.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.IO;
+ using System.Linq;
+ using System.Net;
+ using System.Text;
+ using System.Web;
+ using DotNetOpenAuth.Messaging.Reflection;
+
+ /// <summary>
+ /// Manages sending direct messages to a remote party and receiving responses.
+ /// </summary>
+ public abstract class Channel {
+ /// <summary>
+ /// The maximum allowable size for a 301 Redirect response before we send
+ /// a 200 OK response with a scripted form POST with the parameters instead
+ /// in order to ensure successfully sending a large payload to another server
+ /// that might have a maximum allowable size restriction on its GET request.
+ /// </summary>
+ private static int indirectMessageGetToPostThreshold = 2 * 1024; // 2KB, recommended by OpenID group
+
+ /// <summary>
+ /// The template for indirect messages that require form POST to forward through the user agent.
+ /// </summary>
+ /// <remarks>
+ /// We are intentionally using " instead of the html single quote ' below because
+ /// the HtmlEncode'd values that we inject will only escape the double quote, so
+ /// only the double-quote used around these values is safe.
+ /// </remarks>
+ private static string indirectMessageFormPostFormat = @"
+<html>
+<body onload=""var btn = document.getElementById('submit_button'); btn.disabled = true; btn.value = 'Login in progress'; document.getElementById('openid_message').submit()"">
+<form id=""openid_message"" action=""{0}"" method=""post"" accept-charset=""UTF-8"" enctype=""application/x-www-form-urlencoded"" onSubmit=""var btn = document.getElementById('submit_button'); btn.disabled = true; btn.value = 'Login in progress'; return true;"">
+{1}
+ <input id=""submit_button"" type=""submit"" value=""Continue"" />
+</form>
+</body>
+</html>
+";
+
+ /// <summary>
+ /// A tool that can figure out what kind of message is being received
+ /// so it can be deserialized.
+ /// </summary>
+ private IMessageFactory messageTypeProvider;
+
+ /// <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>();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Channel"/> class.
+ /// </summary>
+ /// <param name="messageTypeProvider">
+ /// A class prepared to analyze incoming messages and indicate what concrete
+ /// message types can deserialize from it.
+ /// </param>
+ /// <param name="bindingElements">The binding elements to use in sending and receiving messages.</param>
+ protected Channel(IMessageFactory messageTypeProvider, params IChannelBindingElement[] bindingElements) {
+ if (messageTypeProvider == null) {
+ throw new ArgumentNullException("messageTypeProvider");
+ }
+
+ this.messageTypeProvider = messageTypeProvider;
+ this.WebRequestHandler = new StandardWebRequestHandler();
+ this.bindingElements = new List<IChannelBindingElement>(ValidateAndPrepareBindingElements(bindingElements));
+
+ foreach (var element in this.bindingElements) {
+ element.Channel = this;
+ }
+ }
+
+ /// <summary>
+ /// An event fired whenever a message is about to be encoded and sent.
+ /// </summary>
+ internal event EventHandler<ChannelEventArgs> Sending;
+
+ /// <summary>
+ /// Gets or sets an instance to a <see cref="IDirectWebRequestHandler"/> that will be used when
+ /// submitting HTTP requests and waiting for responses.
+ /// </summary>
+ /// <remarks>
+ /// This defaults to a straightforward implementation, but can be set
+ /// to a mock object for testing purposes.
+ /// </remarks>
+ public IDirectWebRequestHandler WebRequestHandler { get; set; }
+
+ /// <summary>
+ /// Gets the binding elements used by this channel, in the order they are applied to outgoing messages.
+ /// </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();
+ }
+ }
+
+ /// <summary>
+ /// Gets a tool that can figure out what kind of message is being received
+ /// so it can be deserialized.
+ /// </summary>
+ protected IMessageFactory MessageFactory {
+ get { return this.messageTypeProvider; }
+ }
+
+ /// <summary>
+ /// Queues an indirect message (either a request or response)
+ /// or direct message response for transmission to a remote party.
+ /// </summary>
+ /// <param name="message">The one-way message to send</param>
+ /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
+ public UserAgentResponse Send(IProtocolMessage message) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+ this.PrepareMessageForSending(message);
+ Logger.DebugFormat("Sending message: {0}", message);
+
+ switch (message.Transport) {
+ case MessageTransport.Direct:
+ // This is a response to a direct message.
+ return this.SendDirectMessageResponse(message);
+ case MessageTransport.Indirect:
+ var directedMessage = message as IDirectedProtocolMessage;
+ if (directedMessage == null) {
+ throw new ArgumentException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.IndirectMessagesMustImplementIDirectedProtocolMessage,
+ typeof(IDirectedProtocolMessage).FullName),
+ "message");
+ }
+ if (directedMessage.Recipient == null) {
+ throw new ArgumentException(MessagingStrings.DirectedMessageMissingRecipient, "message");
+ }
+ return this.SendIndirectMessage(directedMessage);
+ default:
+ throw new ArgumentException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.UnrecognizedEnumValue,
+ "Transport",
+ message.Transport),
+ "message");
+ }
+ }
+
+ /// <summary>
+ /// Gets the protocol message embedded in the given HTTP request, if present.
+ /// </summary>
+ /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
+ /// <remarks>
+ /// Requires an HttpContext.Current context.
+ /// </remarks>
+ /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
+ public IDirectedProtocolMessage ReadFromRequest() {
+ return this.ReadFromRequest(this.GetRequestFromContext());
+ }
+
+ /// <summary>
+ /// Gets the protocol message embedded in the given HTTP request, if present.
+ /// </summary>
+ /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
+ /// <param name="request">The deserialized message, if one is found. Null otherwise.</param>
+ /// <returns>True if the expected message was recognized and deserialized. False otherwise.</returns>
+ /// <remarks>
+ /// Requires an HttpContext.Current context.
+ /// </remarks>
+ /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
+ /// <exception cref="ProtocolException">Thrown when a request message of an unexpected type is received.</exception>
+ public bool TryReadFromRequest<TRequest>(out TRequest request)
+ where TRequest : class, IProtocolMessage {
+ return TryReadFromRequest<TRequest>(this.GetRequestFromContext(), out request);
+ }
+
+ /// <summary>
+ /// Gets the protocol message embedded in the given HTTP request, if present.
+ /// </summary>
+ /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
+ /// <param name="httpRequest">The request to search for an embedded message.</param>
+ /// <param name="request">The deserialized message, if one is found. Null otherwise.</param>
+ /// <returns>True if the expected message was recognized and deserialized. False otherwise.</returns>
+ /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
+ /// <exception cref="ProtocolException">Thrown when a request message of an unexpected type is received.</exception>
+ public bool TryReadFromRequest<TRequest>(HttpRequestInfo httpRequest, out TRequest request)
+ where TRequest : class, IProtocolMessage {
+ IProtocolMessage untypedRequest = this.ReadFromRequest(httpRequest);
+ if (untypedRequest == null) {
+ request = null;
+ return false;
+ }
+
+ request = untypedRequest as TRequest;
+ if (request == null) {
+ throw new ProtocolException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.UnexpectedMessageReceived,
+ typeof(TRequest),
+ untypedRequest.GetType()));
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Gets the protocol message embedded in the given HTTP request, if present.
+ /// </summary>
+ /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
+ /// <returns>The deserialized message.</returns>
+ /// <remarks>
+ /// Requires an HttpContext.Current context.
+ /// </remarks>
+ /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
+ /// <exception cref="ProtocolException">Thrown if the expected message was not recognized in the response.</exception>
+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")]
+ public TRequest ReadFromRequest<TRequest>()
+ where TRequest : class, IProtocolMessage {
+ return this.ReadFromRequest<TRequest>(this.GetRequestFromContext());
+ }
+
+ /// <summary>
+ /// Gets the protocol message that may be embedded in the given HTTP request.
+ /// </summary>
+ /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
+ /// <param name="httpRequest">The request to search for an embedded message.</param>
+ /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
+ /// <exception cref="ProtocolException">Thrown if the expected message was not recognized in the response.</exception>
+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")]
+ public TRequest ReadFromRequest<TRequest>(HttpRequestInfo httpRequest)
+ where TRequest : class, IProtocolMessage {
+ TRequest request;
+ if (this.TryReadFromRequest<TRequest>(httpRequest, out request)) {
+ return request;
+ } else {
+ throw new ProtocolException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.ExpectedMessageNotReceived,
+ typeof(TRequest)));
+ }
+ }
+
+ /// <summary>
+ /// Gets the protocol message that may be embedded in the given HTTP request.
+ /// </summary>
+ /// <param name="httpRequest">The request to search for an embedded message.</param>
+ /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
+ public IDirectedProtocolMessage ReadFromRequest(HttpRequestInfo httpRequest) {
+ IDirectedProtocolMessage requestMessage = this.ReadFromRequestInternal(httpRequest);
+ if (requestMessage != null) {
+ Logger.DebugFormat("Incoming request received: {0}", requestMessage);
+ this.VerifyMessageAfterReceiving(requestMessage);
+ }
+
+ return requestMessage;
+ }
+
+ /// <summary>
+ /// Sends a direct message to a remote party and waits for the response.
+ /// </summary>
+ /// <typeparam name="TResponse">The expected type of the message to be received.</typeparam>
+ /// <param name="requestMessage">The message to send.</param>
+ /// <returns>The remote party's response.</returns>
+ /// <exception cref="ProtocolException">
+ /// Thrown if no message is recognized in the response
+ /// or an unexpected type of message is received.
+ /// </exception>
+ [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")]
+ public TResponse Request<TResponse>(IDirectedProtocolMessage requestMessage)
+ where TResponse : class, IProtocolMessage {
+ IProtocolMessage response = this.Request(requestMessage);
+ ErrorUtilities.VerifyProtocol(response != null, MessagingStrings.ExpectedMessageNotReceived, typeof(TResponse));
+
+ var expectedResponse = response as TResponse;
+ ErrorUtilities.VerifyProtocol(expectedResponse != null, MessagingStrings.UnexpectedMessageReceived, typeof(TResponse), response.GetType());
+
+ return expectedResponse;
+ }
+
+ /// <summary>
+ /// Sends a direct message to a remote party and waits for the response.
+ /// </summary>
+ /// <param name="requestMessage">The message to send.</param>
+ /// <returns>The remote party's response. Guaranteed to never be null.</returns>
+ /// <exception cref="ProtocolException">Thrown if the response does not include a protocol message.</exception>
+ public IProtocolMessage Request(IDirectedProtocolMessage requestMessage) {
+ if (requestMessage == null) {
+ throw new ArgumentNullException("requestMessage");
+ }
+
+ this.PrepareMessageForSending(requestMessage);
+ Logger.DebugFormat("Sending request: {0}", requestMessage);
+ var responseMessage = this.RequestInternal(requestMessage);
+ ErrorUtilities.VerifyProtocol(responseMessage != null, MessagingStrings.ExpectedMessageNotReceived, typeof(IProtocolMessage).Name);
+
+ Logger.DebugFormat("Received message response: {0}", responseMessage);
+ this.VerifyMessageAfterReceiving(responseMessage);
+
+ return responseMessage;
+ }
+
+ /// <summary>
+ /// Gets the current HTTP request being processed.
+ /// </summary>
+ /// <returns>The HttpRequestInfo for the current request.</returns>
+ /// <remarks>
+ /// Requires an HttpContext.Current context.
+ /// </remarks>
+ /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current"/> is null.</exception>
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Costly call should not be a property.")]
+ protected internal virtual HttpRequestInfo GetRequestFromContext() {
+ if (HttpContext.Current == null) {
+ throw new InvalidOperationException(MessagingStrings.HttpContextRequired);
+ }
+
+ return new HttpRequestInfo(HttpContext.Current.Request);
+ }
+
+ /// <summary>
+ /// Fires the <see cref="Sending"/> event.
+ /// </summary>
+ /// <param name="message">The message about to be encoded and sent.</param>
+ protected virtual void OnSending(IProtocolMessage message) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ var sending = this.Sending;
+ if (sending != null) {
+ sending(this, new ChannelEventArgs(message));
+ }
+ }
+
+ /// <summary>
+ /// Submits a direct request message to some remote party and blocks waiting for an immediately reply.
+ /// </summary>
+ /// <param name="request">The request message.</param>
+ /// <returns>The response message, or null if the response did not carry a message.</returns>
+ /// <remarks>
+ /// Typically a deriving channel will override <see cref="CreateHttpRequest"/> to customize this method's
+ /// behavior. However in non-HTTP frameworks, such as unit test mocks, it may be appropriate to override
+ /// this method to eliminate all use of an HTTP transport.
+ /// </remarks>
+ protected virtual IProtocolMessage RequestInternal(IDirectedProtocolMessage request) {
+ HttpWebRequest webRequest = this.CreateHttpRequest(request);
+ IDictionary<string, string> responseFields;
+
+ using (DirectWebResponse response = this.WebRequestHandler.GetResponse(webRequest)) {
+ if (response.ResponseStream == null) {
+ return null;
+ }
+
+ responseFields = this.ReadFromResponseInternal(response);
+ }
+
+ IDirectResponseProtocolMessage responseMessage = this.MessageFactory.GetNewResponseMessage(request, responseFields);
+ if (responseMessage == null) {
+ return null;
+ }
+
+ var responseSerialize = MessageSerializer.Get(responseMessage.GetType());
+ responseSerialize.Deserialize(responseFields, responseMessage);
+
+ return responseMessage;
+ }
+
+ /// <summary>
+ /// Gets the protocol message that may be embedded in the given HTTP request.
+ /// </summary>
+ /// <param name="request">The request to search for an embedded message.</param>
+ /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
+ protected virtual IDirectedProtocolMessage ReadFromRequestInternal(HttpRequestInfo request) {
+ if (request == null) {
+ throw new ArgumentNullException("request");
+ }
+
+ // Search Form data first, and if nothing is there search the QueryString
+ var fields = request.Form.ToDictionary();
+ if (fields.Count == 0) {
+ fields = request.QueryString.ToDictionary();
+ }
+
+ return (IDirectedProtocolMessage)this.Receive(fields, request.GetRecipient());
+ }
+
+ /// <summary>
+ /// Deserializes a dictionary of values into a message.
+ /// </summary>
+ /// <param name="fields">The dictionary of values that were read from an HTTP request or response.</param>
+ /// <param name="recipient">Information about where the message was been directed. Null for direct response messages.</param>
+ /// <returns>The deserialized message, or null if no message could be recognized in the provided data.</returns>
+ protected virtual IProtocolMessage Receive(Dictionary<string, string> fields, MessageReceivingEndpoint recipient) {
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+
+ IProtocolMessage message = this.MessageFactory.GetNewRequestMessage(recipient, fields);
+
+ // If there was no data, or we couldn't recognize it as a message, abort.
+ if (message == null) {
+ return null;
+ }
+
+ // We have a message! Assemble it.
+ var serializer = MessageSerializer.Get(message.GetType());
+ serializer.Deserialize(fields, message);
+
+ return message;
+ }
+
+ /// <summary>
+ /// Queues an indirect message for transmittal via the user agent.
+ /// </summary>
+ /// <param name="message">The message to send.</param>
+ /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
+ protected virtual UserAgentResponse SendIndirectMessage(IDirectedProtocolMessage message) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ var serializer = MessageSerializer.Get(message.GetType());
+ var fields = serializer.Serialize(message);
+ UserAgentResponse response;
+ if (CalculateSizeOfPayload(fields) > indirectMessageGetToPostThreshold) {
+ response = this.CreateFormPostResponse(message, fields);
+ } else {
+ response = this.Create301RedirectResponse(message, fields);
+ }
+
+ return response;
+ }
+
+ /// <summary>
+ /// Encodes an HTTP response that will instruct the user agent to forward a message to
+ /// some remote third party using a 301 Redirect GET method.
+ /// </summary>
+ /// <param name="message">The message to forward.</param>
+ /// <param name="fields">The pre-serialized fields from the message.</param>
+ /// <returns>The encoded HTTP response.</returns>
+ protected virtual UserAgentResponse Create301RedirectResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+ if (message.Recipient == null) {
+ throw new ArgumentException(MessagingStrings.DirectedMessageMissingRecipient, "message");
+ }
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+
+ WebHeaderCollection headers = new WebHeaderCollection();
+ UriBuilder builder = new UriBuilder(message.Recipient);
+ MessagingUtilities.AppendQueryArgs(builder, fields);
+ headers.Add(HttpResponseHeader.Location, builder.Uri.AbsoluteUri);
+ Logger.DebugFormat("Redirecting to {0}", builder.Uri.AbsoluteUri);
+ UserAgentResponse response = new UserAgentResponse {
+ Status = HttpStatusCode.Redirect,
+ Headers = headers,
+ Body = null,
+ OriginalMessage = message
+ };
+
+ return response;
+ }
+
+ /// <summary>
+ /// Encodes an HTTP response that will instruct the user agent to forward a message to
+ /// some remote third party using a form POST method.
+ /// </summary>
+ /// <param name="message">The message to forward.</param>
+ /// <param name="fields">The pre-serialized fields from the message.</param>
+ /// <returns>The encoded HTTP response.</returns>
+ protected virtual UserAgentResponse CreateFormPostResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+ if (message.Recipient == null) {
+ throw new ArgumentException(MessagingStrings.DirectedMessageMissingRecipient, "message");
+ }
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+
+ WebHeaderCollection headers = new WebHeaderCollection();
+ StringWriter bodyWriter = new StringWriter(CultureInfo.InvariantCulture);
+ StringBuilder hiddenFields = new StringBuilder();
+ foreach (var field in fields) {
+ hiddenFields.AppendFormat(
+ "\t<input type=\"hidden\" name=\"{0}\" value=\"{1}\" />\r\n",
+ HttpUtility.HtmlEncode(field.Key),
+ HttpUtility.HtmlEncode(field.Value));
+ }
+ bodyWriter.WriteLine(
+ indirectMessageFormPostFormat,
+ HttpUtility.HtmlEncode(message.Recipient.AbsoluteUri),
+ hiddenFields);
+ bodyWriter.Flush();
+ UserAgentResponse response = new UserAgentResponse {
+ Status = HttpStatusCode.OK,
+ Headers = headers,
+ Body = bodyWriter.ToString(),
+ OriginalMessage = message
+ };
+
+ return response;
+ }
+
+ /// <summary>
+ /// Gets the protocol message that may be in the given HTTP response.
+ /// </summary>
+ /// <param name="response">The response that is anticipated to contain an protocol message.</param>
+ /// <returns>The deserialized message parts, if found. Null otherwise.</returns>
+ protected abstract IDictionary<string, string> ReadFromResponseInternal(DirectWebResponse response);
+
+ /// <summary>
+ /// Prepares an HTTP request that carries a given message.
+ /// </summary>
+ /// <param name="request">The message to send.</param>
+ /// <returns>The <see cref="HttpWebRequest"/> prepared to send the request.</returns>
+ /// <remarks>
+ /// This method must be overridden by a derived class, unless the <see cref="RequestInternal"/> method
+ /// is overridden and does not require this method.
+ /// </remarks>
+ protected virtual HttpWebRequest CreateHttpRequest(IDirectedProtocolMessage request) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Queues a message for sending in the response stream where the fields
+ /// are sent in the response stream in querystring style.
+ /// </summary>
+ /// <param name="response">The message to send as a response.</param>
+ /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
+ /// <remarks>
+ /// This method implements spec V1.0 section 5.3.
+ /// </remarks>
+ protected abstract UserAgentResponse SendDirectMessageResponse(IProtocolMessage response);
+
+ /// <summary>
+ /// Prepares a message for transmit by applying signatures, nonces, etc.
+ /// </summary>
+ /// <param name="message">The message to prepare for sending.</param>
+ /// <remarks>
+ /// This method should NOT be called by derived types
+ /// except when sending ONE WAY request messages.
+ /// </remarks>
+ protected void PrepareMessageForSending(IProtocolMessage message) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ this.OnSending(message);
+
+ MessageProtections appliedProtection = MessageProtections.None;
+ foreach (IChannelBindingElement bindingElement in this.bindingElements) {
+ if (bindingElement.PrepareMessageForSending(message)) {
+ appliedProtection |= bindingElement.Protection;
+ }
+ }
+
+ // Ensure that the message's protection requirements have been satisfied.
+ if ((message.RequiredProtection & appliedProtection) != message.RequiredProtection) {
+ throw new UnprotectedMessageException(message, appliedProtection);
+ }
+
+ EnsureValidMessageParts(message);
+ message.EnsureValidMessage();
+ }
+
+ /// <summary>
+ /// Prepares to send a request to the Service Provider as the query string in a GET request.
+ /// </summary>
+ /// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
+ /// <returns>The web request ready to send.</returns>
+ /// <remarks>
+ /// This method is simply a standard HTTP Get request with the message parts serialized to the query string.
+ /// This method satisfies OAuth 1.0 section 5.2, item #3.
+ /// </remarks>
+ protected virtual HttpWebRequest InitializeRequestAsGet(IDirectedProtocolMessage requestMessage) {
+ if (requestMessage == null) {
+ throw new ArgumentNullException("requestMessage");
+ }
+
+ var serializer = MessageSerializer.Get(requestMessage.GetType());
+ var fields = serializer.Serialize(requestMessage);
+
+ UriBuilder builder = new UriBuilder(requestMessage.Recipient);
+ MessagingUtilities.AppendQueryArgs(builder, fields);
+ HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(builder.Uri);
+
+ return httpRequest;
+ }
+
+ /// <summary>
+ /// Prepares to send a request to the Service Provider as the payload of a POST request.
+ /// </summary>
+ /// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
+ /// <returns>The web request ready to send.</returns>
+ /// <remarks>
+ /// This method is simply a standard HTTP POST request with the message parts serialized to the POST entity
+ /// with the application/x-www-form-urlencoded content type
+ /// This method satisfies OAuth 1.0 section 5.2, item #2 and OpenID 2.0 section 4.1.2.
+ /// </remarks>
+ protected virtual HttpWebRequest InitializeRequestAsPost(IDirectedProtocolMessage requestMessage) {
+ if (requestMessage == null) {
+ throw new ArgumentNullException("requestMessage");
+ }
+
+ var serializer = MessageSerializer.Get(requestMessage.GetType());
+ var fields = serializer.Serialize(requestMessage);
+
+ HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(requestMessage.Recipient);
+ httpRequest.Method = "POST";
+ httpRequest.ContentType = "application/x-www-form-urlencoded";
+ string requestBody = MessagingUtilities.CreateQueryString(fields);
+ httpRequest.ContentLength = requestBody.Length;
+ using (TextWriter writer = this.WebRequestHandler.GetRequestStream(httpRequest)) {
+ writer.Write(requestBody);
+ }
+
+ return httpRequest;
+ }
+
+ /// <summary>
+ /// Verifies the integrity and applicability of an incoming message.
+ /// </summary>
+ /// <param name="message">The message just received.</param>
+ /// <exception cref="ProtocolException">
+ /// Thrown when the message is somehow invalid.
+ /// This can be due to tampering, replay attack or expiration, among other things.
+ /// </exception>
+ protected virtual void VerifyMessageAfterReceiving(IProtocolMessage message) {
+ Debug.Assert(message != null, "message == null");
+
+ MessageProtections appliedProtection = MessageProtections.None;
+ foreach (IChannelBindingElement bindingElement in this.bindingElements.Reverse<IChannelBindingElement>()) {
+ if (bindingElement.PrepareMessageForReceiving(message)) {
+ appliedProtection |= bindingElement.Protection;
+ }
+ }
+
+ // Ensure that the message's protection requirements have been satisfied.
+ if ((message.RequiredProtection & appliedProtection) != message.RequiredProtection) {
+ throw new UnprotectedMessageException(message, appliedProtection);
+ }
+
+ // We do NOT verify that all required message parts are present here... the
+ // message deserializer did for us. It would be too late to do it here since
+ // they might look initialized by the time we have an IProtocolMessage instance.
+ message.EnsureValidMessage();
+ }
+
+ /// <summary>
+ /// Verifies that all required message parts are initialized to values
+ /// prior to sending the message to a remote party.
+ /// </summary>
+ /// <param name="message">The message to verify.</param>
+ /// <exception cref="ProtocolException">
+ /// Thrown when any required message part does not have a value.
+ /// </exception>
+ private static void EnsureValidMessageParts(IProtocolMessage message) {
+ Debug.Assert(message != null, "message == null");
+
+ MessageDictionary dictionary = new MessageDictionary(message);
+ MessageDescription description = MessageDescription.Get(message.GetType(), message.Version);
+ description.EnsureMessagePartsPassBasicValidation(dictionary);
+ }
+
+ /// <summary>
+ /// Calculates a fairly accurate estimation on the size of a message that contains
+ /// a given set of fields.
+ /// </summary>
+ /// <param name="fields">The fields that would be included in a message.</param>
+ /// <returns>The size (in bytes) of the message payload.</returns>
+ private static int CalculateSizeOfPayload(IDictionary<string, string> fields) {
+ Debug.Assert(fields != null, "fields == null");
+
+ int size = 0;
+ foreach (var field in fields) {
+ size += field.Key.Length;
+ size += field.Value.Length;
+ size += 2; // & and =
+ }
+ return size;
+ }
+
+ /// <summary>
+ /// Ensures a consistent and secure set of binding elements and
+ /// sorts them as necessary for a valid sequence of operations.
+ /// </summary>
+ /// <param name="elements">The binding elements provided to the channel.</param>
+ /// <returns>The properly ordered list of elements.</returns>
+ /// <exception cref="ProtocolException">Thrown when the binding elements are incomplete or inconsistent with each other.</exception>
+ private static IEnumerable<IChannelBindingElement> ValidateAndPrepareBindingElements(IEnumerable<IChannelBindingElement> elements) {
+ if (elements == null) {
+ return new IChannelBindingElement[0];
+ }
+ if (elements.Contains(null)) {
+ throw new ArgumentException(MessagingStrings.SequenceContainsNullElement, "elements");
+ }
+
+ // Filter the elements between the mere transforming ones and the protection ones.
+ var transformationElements = new List<IChannelBindingElement>(
+ elements.Where(element => element.Protection == MessageProtections.None));
+ var protectionElements = new List<IChannelBindingElement>(
+ elements.Where(element => element.Protection != MessageProtections.None));
+
+ bool wasLastProtectionPresent = true;
+ foreach (MessageProtections protectionKind in Enum.GetValues(typeof(MessageProtections))) {
+ if (protectionKind == MessageProtections.None) {
+ continue;
+ }
+
+ int countProtectionsOfThisKind = protectionElements.Count(element => (element.Protection & protectionKind) == protectionKind);
+
+ // Each protection binding element is backed by the presence of its dependent protection(s).
+ if (countProtectionsOfThisKind > 0 && !wasLastProtectionPresent) {
+ throw new ProtocolException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.RequiredProtectionMissing,
+ 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;
+ }
+
+ // Put the binding elements in order so they are correctly applied to outgoing messages.
+ // Start with the transforming (non-protecting) binding elements first and preserve their original order.
+ var orderedList = new List<IChannelBindingElement>(transformationElements);
+
+ // Now sort the protection binding elements among themselves and add them to the list.
+ orderedList.AddRange(protectionElements.OrderBy(element => element.Protection, BindingElementOutgoingMessageApplicationOrder));
+ return orderedList;
+ }
+
+ /// <summary>
+ /// Puts binding elements in their correct outgoing message processing order.
+ /// </summary>
+ /// <param name="protection1">The first protection type to compare.</param>
+ /// <param name="protection2">The second protection type to compare.</param>
+ /// <returns>
+ /// -1 if <paramref name="element1"/> should be applied to an outgoing message before <paramref name="element2"/>.
+ /// 1 if <paramref name="element2"/> should be applied to an outgoing message before <paramref name="element1"/>.
+ /// 0 if it doesn't matter.
+ /// </returns>
+ private static int BindingElementOutgoingMessageApplicationOrder(MessageProtections protection1, MessageProtections protection2) {
+ Debug.Assert(protection1 != MessageProtections.None || protection2 != MessageProtections.None, "This comparison function should only be used to compare protection binding elements. Otherwise we change the order of user-defined message transformations.");
+
+ // Now put the protection ones in the right order.
+ return -((int)protection1).CompareTo((int)protection2); // descending flag ordinal order
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/IExtensionMessage.cs b/src/DotNetOpenAuth/Messaging/IExtensionMessage.cs
new file mode 100644
index 0000000..e9c11e0
--- /dev/null
+++ b/src/DotNetOpenAuth/Messaging/IExtensionMessage.cs
@@ -0,0 +1,13 @@
+//-----------------------------------------------------------------------
+// <copyright file="IExtensionMessage.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ /// <summary>
+ /// An interface that extension messages must implement.
+ /// </summary>
+ public interface IExtensionMessage : IMessage {
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/IMessage.cs b/src/DotNetOpenAuth/Messaging/IMessage.cs
new file mode 100644
index 0000000..011e51c
--- /dev/null
+++ b/src/DotNetOpenAuth/Messaging/IMessage.cs
@@ -0,0 +1,53 @@
+//-----------------------------------------------------------------------
+// <copyright file="IProtocolMessage.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+
+ /// <summary>
+ /// The interface that classes must implement to be serialized/deserialized
+ /// as protocol or extension messages.
+ /// </summary>
+ public interface IMessage {
+ /// <summary>
+ /// Gets the version of the protocol or extension this message is prepared to implement.
+ /// </summary>
+ Version Version { get; }
+
+ /// <summary>
+ /// Gets the extra, non-standard Protocol parameters included in the message.
+ /// </summary>
+ /// <remarks>
+ /// Implementations of this interface should ensure that this property never returns null.
+ /// </remarks>
+ IDictionary<string, string> ExtraData { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether this message was deserialized as an incoming message.
+ /// </summary>
+ /// <remarks>
+ /// In message type implementations, this property should default to false and will be set
+ /// to true by the messaging system when the message is deserialized as an incoming message.
+ /// </remarks>
+ bool Incoming { get; }
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ void EnsureValidMessage();
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/IProtocolMessage.cs b/src/DotNetOpenAuth/Messaging/IProtocolMessage.cs
index 9060a1e..0bc2ac1 100644
--- a/src/DotNetOpenAuth/Messaging/IProtocolMessage.cs
+++ b/src/DotNetOpenAuth/Messaging/IProtocolMessage.cs
@@ -1,63 +1,27 @@
-//-----------------------------------------------------------------------
-// <copyright file="IProtocolMessage.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Collections.Generic;
- using System.Text;
-
- /// <summary>
- /// The interface that classes must implement to be serialized/deserialized
- /// as OAuth messages.
- /// </summary>
- public interface IProtocolMessage {
- /// <summary>
- /// Gets the version of the protocol this message is prepared to implement.
- /// </summary>
- Version ProtocolVersion { get; }
-
- /// <summary>
- /// Gets the level of protection this message requires.
- /// </summary>
- MessageProtections RequiredProtection { get; }
-
- /// <summary>
- /// Gets a value indicating whether this is a direct or indirect message.
- /// </summary>
- MessageTransport Transport { get; }
-
- /// <summary>
- /// Gets the extra, non-OAuth parameters included in the message.
- /// </summary>
- /// <remarks>
- /// Implementations of this interface should ensure that this property never returns null.
- /// </remarks>
- IDictionary<string, string> ExtraData { get; }
-
- /// <summary>
- /// Gets a value indicating whether this message was deserialized as an incoming message.
- /// </summary>
- /// <remarks>
- /// In message type implementations, this property should default to false and will be set
- /// to true by the messaging system when the message is deserialized as an incoming message.
- /// </remarks>
- bool Incoming { get; }
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- /// <remarks>
- /// <para>Some messages have required fields, or combinations of fields that must relate to each other
- /// in specialized ways. After deserializing a message, this method checks the state of the
- /// message to see if it conforms to the protocol.</para>
- /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
- /// outside this scope of this particular message.</para>
- /// </remarks>
- /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
- void EnsureValidMessage();
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="IProtocolMessage.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+
+ /// <summary>
+ /// The interface that classes must implement to be serialized/deserialized
+ /// as protocol messages.
+ /// </summary>
+ public interface IProtocolMessage : IMessage {
+ /// <summary>
+ /// Gets the level of protection this message requires.
+ /// </summary>
+ MessageProtections RequiredProtection { get; }
+
+ /// <summary>
+ /// Gets a value indicating whether this is a direct or indirect message.
+ /// </summary>
+ MessageTransport Transport { get; }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/IProtocolMessageWithExtensions.cs b/src/DotNetOpenAuth/Messaging/IProtocolMessageWithExtensions.cs
new file mode 100644
index 0000000..7627dd6
--- /dev/null
+++ b/src/DotNetOpenAuth/Messaging/IProtocolMessageWithExtensions.cs
@@ -0,0 +1,25 @@
+//-----------------------------------------------------------------------
+// <copyright file="IProtocolMessageWithExtensions.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// A protocol message that supports adding extensions to the payload for transmission.
+ /// </summary>
+ public interface IProtocolMessageWithExtensions : IProtocolMessage {
+ /// <summary>
+ /// Gets the list of extensions that are included with this message.
+ /// </summary>
+ /// <remarks>
+ /// Implementations of this interface should ensure that this property never returns null.
+ /// </remarks>
+ IList<IExtensionMessage> Extensions { get; }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/MessageSerializer.cs b/src/DotNetOpenAuth/Messaging/MessageSerializer.cs
index cceab3d..4e9a6f8 100644
--- a/src/DotNetOpenAuth/Messaging/MessageSerializer.cs
+++ b/src/DotNetOpenAuth/Messaging/MessageSerializer.cs
@@ -1,100 +1,100 @@
-//-----------------------------------------------------------------------
-// <copyright file="MessageSerializer.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Globalization;
- using System.Reflection;
- using DotNetOpenAuth.Messaging.Reflection;
- using DotNetOpenAuth.OAuth.ChannelElements;
-
- /// <summary>
- /// Serializes/deserializes OAuth messages for/from transit.
- /// </summary>
- internal class MessageSerializer {
- /// <summary>
- /// The specific <see cref="IProtocolMessage"/>-derived type
- /// that will be serialized and deserialized using this class.
- /// </summary>
- private readonly Type messageType;
-
- /// <summary>
- /// Initializes a new instance of the MessageSerializer class.
- /// </summary>
- /// <param name="messageType">The specific <see cref="IProtocolMessage"/>-derived type
- /// that will be serialized and deserialized using this class.</param>
- private MessageSerializer(Type messageType) {
- Debug.Assert(messageType != null, "messageType == null");
-
- if (!typeof(IProtocolMessage).IsAssignableFrom(messageType)) {
- throw new ArgumentException(
- string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.UnexpectedType,
- typeof(IProtocolMessage).FullName,
- messageType.FullName),
- "messageType");
- }
-
- this.messageType = messageType;
- }
-
- /// <summary>
- /// Creates or reuses a message serializer for a given message type.
- /// </summary>
- /// <param name="messageType">The type of message that will be serialized/deserialized.</param>
- /// <returns>A message serializer for the given message type.</returns>
- internal static MessageSerializer Get(Type messageType) {
- if (messageType == null) {
- throw new ArgumentNullException("messageType");
- }
-
- return new MessageSerializer(messageType);
- }
-
- /// <summary>
- /// Reads the data from a message instance and returns a series of name=value pairs for the fields that must be included in the message.
- /// </summary>
- /// <param name="message">The message to be serialized.</param>
- /// <returns>The dictionary of values to send for the message.</returns>
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Parallel design with Deserialize method.")]
- internal IDictionary<string, string> Serialize(IProtocolMessage message) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
-
- var result = new Reflection.MessageDictionary(message);
-
- return result;
- }
-
- /// <summary>
- /// Reads name=value pairs into an OAuth message.
- /// </summary>
- /// <param name="fields">The name=value pairs that were read in from the transport.</param>
- /// <param name="message">The message to deserialize into.</param>
- /// <exception cref="ProtocolException">Thrown when protocol rules are broken by the incoming message.</exception>
- internal void Deserialize(IDictionary<string, string> fields, IProtocolMessage message) {
- ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
-
- // Before we deserialize the message, make sure all the required parts are present.
- MessageDescription.Get(this.messageType, message.ProtocolVersion).EnsureMessagePartsPassBasicValidation(fields);
-
- try {
- foreach (var pair in fields) {
- IDictionary<string, string> dictionary = new MessageDictionary(message);
- dictionary[pair.Key] = pair.Value;
- }
- } catch (ArgumentException ex) {
- throw ErrorUtilities.Wrap(ex, MessagingStrings.ErrorDeserializingMessage, this.messageType.Name);
- }
- message.EnsureValidMessage();
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="MessageSerializer.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Globalization;
+ using System.Reflection;
+ using DotNetOpenAuth.Messaging.Reflection;
+ using DotNetOpenAuth.OAuth.ChannelElements;
+
+ /// <summary>
+ /// Serializes/deserializes OAuth messages for/from transit.
+ /// </summary>
+ internal class MessageSerializer {
+ /// <summary>
+ /// The specific <see cref="IProtocolMessage"/>-derived type
+ /// that will be serialized and deserialized using this class.
+ /// </summary>
+ private readonly Type messageType;
+
+ /// <summary>
+ /// Initializes a new instance of the MessageSerializer class.
+ /// </summary>
+ /// <param name="messageType">The specific <see cref="IProtocolMessage"/>-derived type
+ /// that will be serialized and deserialized using this class.</param>
+ private MessageSerializer(Type messageType) {
+ Debug.Assert(messageType != null, "messageType == null");
+
+ if (!typeof(IProtocolMessage).IsAssignableFrom(messageType)) {
+ throw new ArgumentException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.UnexpectedType,
+ typeof(IProtocolMessage).FullName,
+ messageType.FullName),
+ "messageType");
+ }
+
+ this.messageType = messageType;
+ }
+
+ /// <summary>
+ /// Creates or reuses a message serializer for a given message type.
+ /// </summary>
+ /// <param name="messageType">The type of message that will be serialized/deserialized.</param>
+ /// <returns>A message serializer for the given message type.</returns>
+ internal static MessageSerializer Get(Type messageType) {
+ if (messageType == null) {
+ throw new ArgumentNullException("messageType");
+ }
+
+ return new MessageSerializer(messageType);
+ }
+
+ /// <summary>
+ /// Reads the data from a message instance and returns a series of name=value pairs for the fields that must be included in the message.
+ /// </summary>
+ /// <param name="message">The message to be serialized.</param>
+ /// <returns>The dictionary of values to send for the message.</returns>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Parallel design with Deserialize method.")]
+ internal IDictionary<string, string> Serialize(IProtocolMessage message) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ var result = new Reflection.MessageDictionary(message);
+
+ return result;
+ }
+
+ /// <summary>
+ /// Reads name=value pairs into an OAuth message.
+ /// </summary>
+ /// <param name="fields">The name=value pairs that were read in from the transport.</param>
+ /// <param name="message">The message to deserialize into.</param>
+ /// <exception cref="ProtocolException">Thrown when protocol rules are broken by the incoming message.</exception>
+ internal void Deserialize(IDictionary<string, string> fields, IProtocolMessage message) {
+ ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
+ ErrorUtilities.VerifyArgumentNotNull(message, "message");
+
+ // Before we deserialize the message, make sure all the required parts are present.
+ MessageDescription.Get(this.messageType, message.Version).EnsureMessagePartsPassBasicValidation(fields);
+
+ try {
+ foreach (var pair in fields) {
+ IDictionary<string, string> dictionary = new MessageDictionary(message);
+ dictionary[pair.Key] = pair.Value;
+ }
+ } catch (ArgumentException ex) {
+ throw ErrorUtilities.Wrap(ex, MessagingStrings.ErrorDeserializingMessage, this.messageType.Name);
+ }
+ message.EnsureValidMessage();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
index 13da0a4..96ec91b 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
@@ -1,473 +1,471 @@
-//-----------------------------------------------------------------------
-// <copyright file="MessagingUtilities.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Collections.Generic;
- using System.Collections.Specialized;
- using System.Diagnostics.CodeAnalysis;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Security.Cryptography;
- using System.Text;
- using System.Web;
- using DotNetOpenAuth.Messaging.Reflection;
-
- /// <summary>
- /// A grab-bag of utility methods useful for the channel stack of the protocol.
- /// </summary>
- public static class MessagingUtilities {
- /// <summary>
- /// The cryptographically strong random data generator used for creating secrets.
- /// </summary>
- /// <remarks>The random number generator is thread-safe.</remarks>
- internal static readonly RandomNumberGenerator CryptoRandomDataGenerator = new RNGCryptoServiceProvider();
-
- /// <summary>
- /// Gets the original request URL, as seen from the browser before any URL rewrites on the server if any.
- /// Cookieless session directory (if applicable) is also included.
- /// </summary>
- /// <returns>The URL in the user agent's Location bar.</returns>
- [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "The Uri merging requires use of a string value.")]
- [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Expensive call should not be a property.")]
- public static Uri GetRequestUrlFromContext() {
- HttpContext context = HttpContext.Current;
- if (context == null) {
- throw new InvalidOperationException(MessagingStrings.CurrentHttpContextRequired);
- }
-
- // We use Request.Url for the full path to the server, and modify it
- // with Request.RawUrl to capture both the cookieless session "directory" if it exists
- // and the original path in case URL rewriting is going on. We don't want to be
- // fooled by URL rewriting because we're comparing the actual URL with what's in
- // the return_to parameter in some cases.
- // Response.ApplyAppPathModifier(builder.Path) would have worked for the cookieless
- // session, but not the URL rewriting problem.
- return new Uri(context.Request.Url, context.Request.RawUrl);
- }
-
- /// <summary>
- /// Strips any and all URI query parameters that start with some prefix.
- /// </summary>
- /// <param name="uri">The URI that may have a query with parameters to remove.</param>
- /// <param name="prefix">The prefix for parameters to remove.</param>
- /// <returns>Either a new Uri with the parameters removed if there were any to remove, or the same Uri instance if no parameters needed to be removed.</returns>
- public static Uri StripQueryArgumentsWithPrefix(this Uri uri, string prefix) {
- NameValueCollection queryArgs = HttpUtility.ParseQueryString(uri.Query);
- var matchingKeys = queryArgs.Keys.OfType<string>().Where(key => key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)).ToList();
- if (matchingKeys.Count > 0) {
- UriBuilder builder = new UriBuilder(uri);
- foreach (string key in matchingKeys) {
- queryArgs.Remove(key);
- }
- builder.Query = CreateQueryString(queryArgs.ToDictionary());
- return builder.Uri;
- } else {
- return uri;
- }
- }
-
- /// <summary>
- /// Gets a cryptographically strong random sequence of values.
- /// </summary>
- /// <param name="length">The length of the sequence to generate.</param>
- /// <returns>The generated values, which may contain zeros.</returns>
- internal static byte[] GetCryptoRandomData(int length) {
- byte[] buffer = new byte[length];
- CryptoRandomDataGenerator.GetBytes(buffer);
- return buffer;
- }
-
- /// <summary>
- /// Gets a cryptographically strong random sequence of values.
- /// </summary>
- /// <param name="binaryLength">The length of the byte sequence to generate.</param>
- /// <returns>A base64 encoding of the generated random data,
- /// whose length in characters will likely be greater than <paramref name="binaryLength"/>.</returns>
- internal static string GetCryptoRandomDataAsBase64(int binaryLength) {
- byte[] uniq_bytes = GetCryptoRandomData(binaryLength);
- string uniq = Convert.ToBase64String(uniq_bytes);
- return uniq;
- }
-
- /// <summary>
- /// Adds a set of HTTP headers to an <see cref="HttpResponse"/> instance,
- /// taking care to set some headers to the appropriate properties of
- /// <see cref="HttpResponse" />
- /// </summary>
- /// <param name="headers">The headers to add.</param>
- /// <param name="response">The <see cref="HttpResponse"/> instance to set the appropriate values to.</param>
- internal static void ApplyHeadersToResponse(WebHeaderCollection headers, HttpResponse response) {
- if (headers == null) {
- throw new ArgumentNullException("headers");
- }
- if (response == null) {
- throw new ArgumentNullException("response");
- }
- foreach (string headerName in headers) {
- switch (headerName) {
- case "Content-Type":
- response.ContentType = headers[HttpResponseHeader.ContentType];
- break;
-
- // Add more special cases here as necessary.
- default:
- response.AddHeader(headerName, headers[headerName]);
- break;
- }
- }
- }
-
- /// <summary>
- /// Copies the contents of one stream to another.
- /// </summary>
- /// <param name="copyFrom">The stream to copy from, at the position where copying should begin.</param>
- /// <param name="copyTo">The stream to copy to, at the position where bytes should be written.</param>
- /// <returns>The total number of bytes copied.</returns>
- /// <remarks>
- /// Copying begins at the streams' current positions.
- /// The positions are NOT reset after copying is complete.
- /// </remarks>
- internal static int CopyTo(this Stream copyFrom, Stream copyTo) {
- return CopyTo(copyFrom, copyTo, int.MaxValue);
- }
-
- /// <summary>
- /// Copies the contents of one stream to another.
- /// </summary>
- /// <param name="copyFrom">The stream to copy from, at the position where copying should begin.</param>
- /// <param name="copyTo">The stream to copy to, at the position where bytes should be written.</param>
- /// <param name="maximumBytesToCopy">The maximum bytes to copy.</param>
- /// <returns>The total number of bytes copied.</returns>
- /// <remarks>
- /// Copying begins at the streams' current positions.
- /// The positions are NOT reset after copying is complete.
- /// </remarks>
- internal static int CopyTo(this Stream copyFrom, Stream copyTo, int maximumBytesToCopy) {
- ErrorUtilities.VerifyArgumentNotNull(copyFrom, "copyFrom");
- ErrorUtilities.VerifyArgumentNotNull(copyTo, "copyTo");
- ErrorUtilities.VerifyArgument(copyFrom.CanRead, MessagingStrings.StreamUnreadable);
- ErrorUtilities.VerifyArgument(copyTo.CanWrite, MessagingStrings.StreamUnwritable, "copyTo");
-
- byte[] buffer = new byte[1024];
- int readBytes;
- int totalCopiedBytes = 0;
- while ((readBytes = copyFrom.Read(buffer, 0, Math.Min(1024, maximumBytesToCopy))) > 0) {
- int writeBytes = Math.Min(maximumBytesToCopy, readBytes);
- copyTo.Write(buffer, 0, writeBytes);
- totalCopiedBytes += writeBytes;
- maximumBytesToCopy -= writeBytes;
- }
-
- return totalCopiedBytes;
- }
-
- /// <summary>
- /// Creates a snapshot of some stream so it is seekable, and the original can be closed.
- /// </summary>
- /// <param name="copyFrom">The stream to copy bytes from.</param>
- /// <returns>A seekable stream with the same contents as the original.</returns>
- internal static Stream CreateSnapshot(this Stream copyFrom) {
- ErrorUtilities.VerifyArgumentNotNull(copyFrom, "copyFrom");
-
- MemoryStream copyTo = new MemoryStream(copyFrom.CanSeek ? (int)copyFrom.Length : 4 * 1024);
- copyFrom.CopyTo(copyTo);
- copyTo.Position = 0;
- return copyTo;
- }
-
- /// <summary>
- /// Clones an <see cref="HttpWebRequest"/> in order to send it again.
- /// </summary>
- /// <param name="request">The request to clone.</param>
- /// <returns>The newly created instance.</returns>
- internal static HttpWebRequest Clone(this HttpWebRequest request) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
- return Clone(request, request.RequestUri);
- }
-
- /// <summary>
- /// Clones an <see cref="HttpWebRequest"/> in order to send it again.
- /// </summary>
- /// <param name="request">The request to clone.</param>
- /// <param name="newRequestUri">The new recipient of the request.</param>
- /// <returns>The newly created instance.</returns>
- internal static HttpWebRequest Clone(this HttpWebRequest request, Uri newRequestUri) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
- ErrorUtilities.VerifyArgumentNotNull(newRequestUri, "newRequestUri");
-
- var newRequest = (HttpWebRequest)WebRequest.Create(newRequestUri);
- newRequest.Accept = request.Accept;
- newRequest.AllowAutoRedirect = request.AllowAutoRedirect;
- newRequest.AllowWriteStreamBuffering = request.AllowWriteStreamBuffering;
- newRequest.AuthenticationLevel = request.AuthenticationLevel;
- newRequest.AutomaticDecompression = request.AutomaticDecompression;
- newRequest.CachePolicy = request.CachePolicy;
- newRequest.ClientCertificates = request.ClientCertificates;
- newRequest.ConnectionGroupName = request.ConnectionGroupName;
- if (request.ContentLength >= 0) {
- newRequest.ContentLength = request.ContentLength;
- }
- newRequest.ContentType = request.ContentType;
- newRequest.ContinueDelegate = request.ContinueDelegate;
- newRequest.CookieContainer = request.CookieContainer;
- newRequest.Credentials = request.Credentials;
- newRequest.Expect = request.Expect;
- newRequest.IfModifiedSince = request.IfModifiedSince;
- newRequest.ImpersonationLevel = request.ImpersonationLevel;
- newRequest.KeepAlive = request.KeepAlive;
- newRequest.MaximumAutomaticRedirections = request.MaximumAutomaticRedirections;
- newRequest.MaximumResponseHeadersLength = request.MaximumResponseHeadersLength;
- newRequest.MediaType = request.MediaType;
- newRequest.Method = request.Method;
- newRequest.Pipelined = request.Pipelined;
- newRequest.PreAuthenticate = request.PreAuthenticate;
- newRequest.ProtocolVersion = request.ProtocolVersion;
- newRequest.Proxy = request.Proxy;
- newRequest.ReadWriteTimeout = request.ReadWriteTimeout;
- newRequest.Referer = request.Referer;
- newRequest.SendChunked = request.SendChunked;
- newRequest.Timeout = request.Timeout;
- newRequest.TransferEncoding = request.TransferEncoding;
- newRequest.UnsafeAuthenticatedConnectionSharing = request.UnsafeAuthenticatedConnectionSharing;
- newRequest.UseDefaultCredentials = request.UseDefaultCredentials;
- newRequest.UserAgent = request.UserAgent;
-
- // We copy headers last, and only those that do not yet exist as a result
- // of setting these properties, so as to avoid exceptions thrown because
- // there are properties .NET wants us to use rather than direct headers.
- foreach (string header in request.Headers) {
- if (string.IsNullOrEmpty(newRequest.Headers[header])) {
- newRequest.Headers.Add(header, request.Headers[header]);
- }
- }
-
- return newRequest;
- }
-
- /// <summary>
- /// Tests whether two arrays are equal in length and contents.
- /// </summary>
- /// <typeparam name="T">The type of elements in the arrays.</typeparam>
- /// <param name="first">The first array in the comparison. May not be null.</param>
- /// <param name="second">The second array in the comparison. May not be null.</param>
- /// <returns>True if the arrays equal; false otherwise.</returns>
- internal static bool AreEquivalent<T>(T[] first, T[] second) {
- if (first == null) {
- throw new ArgumentNullException("first");
- }
- if (second == null) {
- throw new ArgumentNullException("second");
- }
- if (first.Length != second.Length) {
- return false;
- }
- for (int i = 0; i < first.Length; i++) {
- if (!first[i].Equals(second[i])) {
- return false;
- }
- }
- return true;
- }
-
- /// <summary>
- /// Tests whether two dictionaries are equal in length and contents.
- /// </summary>
- /// <typeparam name="TKey">The type of keys in the dictionaries.</typeparam>
- /// <typeparam name="TValue">The type of values in the dictionaries.</typeparam>
- /// <param name="first">The first dictionary in the comparison. May not be null.</param>
- /// <param name="second">The second dictionary in the comparison. May not be null.</param>
- /// <returns>True if the arrays equal; false otherwise.</returns>
- internal static bool AreEquivalent<TKey, TValue>(IDictionary<TKey, TValue> first, IDictionary<TKey, TValue> second) {
- return AreEquivalent(first.ToArray(), second.ToArray());
- }
-
- /// <summary>
- /// Concatenates a list of name-value pairs as key=value&amp;key=value,
- /// taking care to properly encode each key and value for URL
- /// transmission. No ? is prefixed to the string.
- /// </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) {
- return string.Empty;
- }
- StringBuilder sb = new StringBuilder(args.Count * 10);
-
- foreach (var p in args) {
- sb.Append(HttpUtility.UrlEncode(p.Key));
- sb.Append('=');
- sb.Append(HttpUtility.UrlEncode(p.Value));
- sb.Append('&');
- }
- sb.Length--; // remove trailing &
-
- return sb.ToString();
- }
-
- /// <summary>
- /// Adds a set of name-value pairs to the end of a given URL
- /// as part of the querystring piece. Prefixes a ? or &amp; before
- /// first element as necessary.
- /// </summary>
- /// <param name="builder">The UriBuilder to add arguments to.</param>
- /// <param name="args">
- /// 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) {
- if (builder == null) {
- throw new ArgumentNullException("builder");
- }
-
- 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('&');
- }
- sb.Append(CreateQueryString(args));
-
- builder.Query = sb.ToString();
- }
- }
-
- /// <summary>
- /// Extracts the recipient from an HttpRequestInfo.
- /// </summary>
- /// <param name="request">The request to get recipient information from.</param>
- /// <returns>The recipient.</returns>
- internal static MessageReceivingEndpoint GetRecipient(this HttpRequestInfo request) {
- return new MessageReceivingEndpoint(request.Url, request.HttpMethod == "GET" ? HttpDeliveryMethods.GetRequest : HttpDeliveryMethods.PostRequest);
- }
-
- /// <summary>
- /// Copies some extra parameters into a message.
- /// </summary>
- /// <param name="message">The message to copy the extra data into.</param>
- /// <param name="extraParameters">The extra data to copy into the message. May be null to do nothing.</param>
- internal static void AddNonOAuthParameters(this IProtocolMessage message, IDictionary<string, string> extraParameters) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
-
- if (extraParameters != null) {
- MessageDictionary messageDictionary = new MessageDictionary(message);
- foreach (var pair in extraParameters) {
- messageDictionary.Add(pair);
- }
- }
- }
-
- /// <summary>
- /// Converts a <see cref="NameValueCollection"/> to an IDictionary&lt;string, string&gt;.
- /// </summary>
- /// <param name="nvc">The NameValueCollection to convert. May be null.</param>
- /// <returns>The generated dictionary, or null if <paramref name="nvc"/> is null.</returns>
- internal static Dictionary<string, string> ToDictionary(this NameValueCollection nvc) {
- if (nvc == null) {
- return null;
- }
-
- var dictionary = new Dictionary<string, string>();
- foreach (string key in nvc) {
- dictionary.Add(key, nvc[key]);
- }
-
- return dictionary;
- }
-
- /// <summary>
- /// Sorts the elements of a sequence in ascending order by using a specified comparer.
- /// </summary>
- /// <typeparam name="TSource">The type of the elements of source.</typeparam>
- /// <typeparam name="TKey">The type of the key returned by keySelector.</typeparam>
- /// <param name="source">A sequence of values to order.</param>
- /// <param name="keySelector">A function to extract a key from an element.</param>
- /// <param name="comparer">A comparison function to compare keys.</param>
- /// <returns>An System.Linq.IOrderedEnumerable&lt;TElement&gt; whose elements are sorted according to a key.</returns>
- internal static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Comparison<TKey> comparer) {
- return System.Linq.Enumerable.OrderBy<TSource, TKey>(source, keySelector, new ComparisonHelper<TKey>(comparer));
- }
-
- /// <summary>
- /// Determines whether the specified message is a request (indirect message or direct request).
- /// </summary>
- /// <param name="message">The message in question.</param>
- /// <returns>
- /// <c>true</c> if the specified message is a request; otherwise, <c>false</c>.
- /// </returns>
- /// <remarks>
- /// Although an <see cref="IProtocolMessage"/> may implement the <see cref="IDirectedProtocolMessage"/>
- /// interface, it may only be doing that for its derived classes. These objects are only requests
- /// if their <see cref="IDirectedProtocolMessage.Recipient"/> property is non-null.
- /// </remarks>
- internal static bool IsRequest(this IDirectedProtocolMessage message) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
- return message.Recipient != null;
- }
-
- /// <summary>
- /// Determines whether the specified message is a direct response.
- /// </summary>
- /// <param name="message">The message in question.</param>
- /// <returns>
- /// <c>true</c> if the specified message is a direct response; otherwise, <c>false</c>.
- /// </returns>
- /// <remarks>
- /// Although an <see cref="IProtocolMessage"/> may implement the
- /// <see cref="IDirectResponseProtocolMessage"/> interface, it may only be doing
- /// that for its derived classes. These objects are only requests if their
- /// <see cref="IDirectResponseProtocolMessage.OriginatingRequest"/> property is non-null.
- /// </remarks>
- internal static bool IsDirectResponse(this IDirectResponseProtocolMessage message) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
- return message.OriginatingRequest != null;
- }
-
- /// <summary>
- /// A class to convert a <see cref="Comparison&lt;T&gt;"/> into an <see cref="IComparer&lt;T&gt;"/>.
- /// </summary>
- /// <typeparam name="T">The type of objects being compared.</typeparam>
- private class ComparisonHelper<T> : IComparer<T> {
- /// <summary>
- /// The comparison method to use.
- /// </summary>
- private Comparison<T> comparison;
-
- /// <summary>
- /// Initializes a new instance of the ComparisonHelper class.
- /// </summary>
- /// <param name="comparison">The comparison method to use.</param>
- internal ComparisonHelper(Comparison<T> comparison) {
- if (comparison == null) {
- throw new ArgumentNullException("comparison");
- }
-
- this.comparison = comparison;
- }
-
- #region IComparer<T> Members
-
- /// <summary>
- /// Compares two instances of <typeparamref name="T"/>.
- /// </summary>
- /// <param name="x">The first object to compare.</param>
- /// <param name="y">The second object to compare.</param>
- /// <returns>Any of -1, 0, or 1 according to standard comparison rules.</returns>
- public int Compare(T x, T y) {
- return this.comparison(x, y);
- }
-
- #endregion
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="MessagingUtilities.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+ using System.Diagnostics.CodeAnalysis;
+ using System.IO;
+ using System.Linq;
+ using System.Net;
+ using System.Security.Cryptography;
+ using System.Text;
+ using System.Web;
+ using DotNetOpenAuth.Messaging.Reflection;
+
+ /// <summary>
+ /// A grab-bag of utility methods useful for the channel stack of the protocol.
+ /// </summary>
+ public static class MessagingUtilities {
+ /// <summary>
+ /// The cryptographically strong random data generator used for creating secrets.
+ /// </summary>
+ /// <remarks>The random number generator is thread-safe.</remarks>
+ internal static readonly RandomNumberGenerator CryptoRandomDataGenerator = new RNGCryptoServiceProvider();
+
+ /// <summary>
+ /// Gets the original request URL, as seen from the browser before any URL rewrites on the server if any.
+ /// Cookieless session directory (if applicable) is also included.
+ /// </summary>
+ /// <returns>The URL in the user agent's Location bar.</returns>
+ [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "The Uri merging requires use of a string value.")]
+ [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Expensive call should not be a property.")]
+ public static Uri GetRequestUrlFromContext() {
+ HttpContext context = HttpContext.Current;
+ if (context == null) {
+ throw new InvalidOperationException(MessagingStrings.CurrentHttpContextRequired);
+ }
+
+ // We use Request.Url for the full path to the server, and modify it
+ // with Request.RawUrl to capture both the cookieless session "directory" if it exists
+ // and the original path in case URL rewriting is going on. We don't want to be
+ // fooled by URL rewriting because we're comparing the actual URL with what's in
+ // the return_to parameter in some cases.
+ // Response.ApplyAppPathModifier(builder.Path) would have worked for the cookieless
+ // session, but not the URL rewriting problem.
+ return new Uri(context.Request.Url, context.Request.RawUrl);
+ }
+
+ /// <summary>
+ /// Strips any and all URI query parameters that start with some prefix.
+ /// </summary>
+ /// <param name="uri">The URI that may have a query with parameters to remove.</param>
+ /// <param name="prefix">The prefix for parameters to remove.</param>
+ /// <returns>Either a new Uri with the parameters removed if there were any to remove, or the same Uri instance if no parameters needed to be removed.</returns>
+ public static Uri StripQueryArgumentsWithPrefix(this Uri uri, string prefix) {
+ NameValueCollection queryArgs = HttpUtility.ParseQueryString(uri.Query);
+ var matchingKeys = queryArgs.Keys.OfType<string>().Where(key => key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)).ToList();
+ if (matchingKeys.Count > 0) {
+ UriBuilder builder = new UriBuilder(uri);
+ foreach (string key in matchingKeys) {
+ queryArgs.Remove(key);
+ }
+ builder.Query = CreateQueryString(queryArgs.ToDictionary());
+ return builder.Uri;
+ } else {
+ return uri;
+ }
+ }
+
+ /// <summary>
+ /// Gets a cryptographically strong random sequence of values.
+ /// </summary>
+ /// <param name="length">The length of the sequence to generate.</param>
+ /// <returns>The generated values, which may contain zeros.</returns>
+ internal static byte[] GetCryptoRandomData(int length) {
+ byte[] buffer = new byte[length];
+ CryptoRandomDataGenerator.GetBytes(buffer);
+ return buffer;
+ }
+
+ /// <summary>
+ /// Gets a cryptographically strong random sequence of values.
+ /// </summary>
+ /// <param name="binaryLength">The length of the byte sequence to generate.</param>
+ /// <returns>A base64 encoding of the generated random data,
+ /// whose length in characters will likely be greater than <paramref name="binaryLength"/>.</returns>
+ internal static string GetCryptoRandomDataAsBase64(int binaryLength) {
+ byte[] uniq_bytes = GetCryptoRandomData(binaryLength);
+ string uniq = Convert.ToBase64String(uniq_bytes);
+ return uniq;
+ }
+
+ /// <summary>
+ /// Adds a set of HTTP headers to an <see cref="HttpResponse"/> instance,
+ /// taking care to set some headers to the appropriate properties of
+ /// <see cref="HttpResponse" />
+ /// </summary>
+ /// <param name="headers">The headers to add.</param>
+ /// <param name="response">The <see cref="HttpResponse"/> instance to set the appropriate values to.</param>
+ internal static void ApplyHeadersToResponse(WebHeaderCollection headers, HttpResponse response) {
+ if (headers == null) {
+ throw new ArgumentNullException("headers");
+ }
+ if (response == null) {
+ throw new ArgumentNullException("response");
+ }
+ foreach (string headerName in headers) {
+ switch (headerName) {
+ case "Content-Type":
+ response.ContentType = headers[HttpResponseHeader.ContentType];
+ break;
+
+ // Add more special cases here as necessary.
+ default:
+ response.AddHeader(headerName, headers[headerName]);
+ break;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Copies the contents of one stream to another.
+ /// </summary>
+ /// <param name="copyFrom">The stream to copy from, at the position where copying should begin.</param>
+ /// <param name="copyTo">The stream to copy to, at the position where bytes should be written.</param>
+ /// <returns>The total number of bytes copied.</returns>
+ /// <remarks>
+ /// Copying begins at the streams' current positions.
+ /// The positions are NOT reset after copying is complete.
+ /// </remarks>
+ internal static int CopyTo(this Stream copyFrom, Stream copyTo) {
+ return CopyTo(copyFrom, copyTo, int.MaxValue);
+ }
+
+ /// <summary>
+ /// Copies the contents of one stream to another.
+ /// </summary>
+ /// <param name="copyFrom">The stream to copy from, at the position where copying should begin.</param>
+ /// <param name="copyTo">The stream to copy to, at the position where bytes should be written.</param>
+ /// <param name="maximumBytesToCopy">The maximum bytes to copy.</param>
+ /// <returns>The total number of bytes copied.</returns>
+ /// <remarks>
+ /// Copying begins at the streams' current positions.
+ /// The positions are NOT reset after copying is complete.
+ /// </remarks>
+ internal static int CopyTo(this Stream copyFrom, Stream copyTo, int maximumBytesToCopy) {
+ ErrorUtilities.VerifyArgumentNotNull(copyFrom, "copyFrom");
+ ErrorUtilities.VerifyArgumentNotNull(copyTo, "copyTo");
+ ErrorUtilities.VerifyArgument(copyFrom.CanRead, MessagingStrings.StreamUnreadable);
+ ErrorUtilities.VerifyArgument(copyTo.CanWrite, MessagingStrings.StreamUnwritable, "copyTo");
+
+ byte[] buffer = new byte[1024];
+ int readBytes;
+ int totalCopiedBytes = 0;
+ while ((readBytes = copyFrom.Read(buffer, 0, Math.Min(1024, maximumBytesToCopy))) > 0) {
+ int writeBytes = Math.Min(maximumBytesToCopy, readBytes);
+ copyTo.Write(buffer, 0, writeBytes);
+ totalCopiedBytes += writeBytes;
+ maximumBytesToCopy -= writeBytes;
+ }
+
+ return totalCopiedBytes;
+ }
+
+ /// <summary>
+ /// Creates a snapshot of some stream so it is seekable, and the original can be closed.
+ /// </summary>
+ /// <param name="copyFrom">The stream to copy bytes from.</param>
+ /// <returns>A seekable stream with the same contents as the original.</returns>
+ internal static Stream CreateSnapshot(this Stream copyFrom) {
+ ErrorUtilities.VerifyArgumentNotNull(copyFrom, "copyFrom");
+
+ MemoryStream copyTo = new MemoryStream(copyFrom.CanSeek ? (int)copyFrom.Length : 4 * 1024);
+ copyFrom.CopyTo(copyTo);
+ copyTo.Position = 0;
+ return copyTo;
+ }
+
+ /// <summary>
+ /// Clones an <see cref="HttpWebRequest"/> in order to send it again.
+ /// </summary>
+ /// <param name="request">The request to clone.</param>
+ /// <returns>The newly created instance.</returns>
+ internal static HttpWebRequest Clone(this HttpWebRequest request) {
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ return Clone(request, request.RequestUri);
+ }
+
+ /// <summary>
+ /// Clones an <see cref="HttpWebRequest"/> in order to send it again.
+ /// </summary>
+ /// <param name="request">The request to clone.</param>
+ /// <param name="newRequestUri">The new recipient of the request.</param>
+ /// <returns>The newly created instance.</returns>
+ internal static HttpWebRequest Clone(this HttpWebRequest request, Uri newRequestUri) {
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ ErrorUtilities.VerifyArgumentNotNull(newRequestUri, "newRequestUri");
+
+ var newRequest = (HttpWebRequest)WebRequest.Create(newRequestUri);
+ newRequest.Accept = request.Accept;
+ newRequest.AllowAutoRedirect = request.AllowAutoRedirect;
+ newRequest.AllowWriteStreamBuffering = request.AllowWriteStreamBuffering;
+ newRequest.AuthenticationLevel = request.AuthenticationLevel;
+ newRequest.AutomaticDecompression = request.AutomaticDecompression;
+ newRequest.CachePolicy = request.CachePolicy;
+ newRequest.ClientCertificates = request.ClientCertificates;
+ newRequest.ConnectionGroupName = request.ConnectionGroupName;
+ if (request.ContentLength >= 0) {
+ newRequest.ContentLength = request.ContentLength;
+ }
+ newRequest.ContentType = request.ContentType;
+ newRequest.ContinueDelegate = request.ContinueDelegate;
+ newRequest.CookieContainer = request.CookieContainer;
+ newRequest.Credentials = request.Credentials;
+ newRequest.Expect = request.Expect;
+ newRequest.IfModifiedSince = request.IfModifiedSince;
+ newRequest.ImpersonationLevel = request.ImpersonationLevel;
+ newRequest.KeepAlive = request.KeepAlive;
+ newRequest.MaximumAutomaticRedirections = request.MaximumAutomaticRedirections;
+ newRequest.MaximumResponseHeadersLength = request.MaximumResponseHeadersLength;
+ newRequest.MediaType = request.MediaType;
+ newRequest.Method = request.Method;
+ newRequest.Pipelined = request.Pipelined;
+ newRequest.PreAuthenticate = request.PreAuthenticate;
+ newRequest.ProtocolVersion = request.ProtocolVersion;
+ newRequest.Proxy = request.Proxy;
+ newRequest.ReadWriteTimeout = request.ReadWriteTimeout;
+ newRequest.Referer = request.Referer;
+ newRequest.SendChunked = request.SendChunked;
+ newRequest.Timeout = request.Timeout;
+ newRequest.TransferEncoding = request.TransferEncoding;
+ newRequest.UnsafeAuthenticatedConnectionSharing = request.UnsafeAuthenticatedConnectionSharing;
+ newRequest.UseDefaultCredentials = request.UseDefaultCredentials;
+ newRequest.UserAgent = request.UserAgent;
+
+ // We copy headers last, and only those that do not yet exist as a result
+ // of setting these properties, so as to avoid exceptions thrown because
+ // there are properties .NET wants us to use rather than direct headers.
+ foreach (string header in request.Headers) {
+ if (string.IsNullOrEmpty(newRequest.Headers[header])) {
+ newRequest.Headers.Add(header, request.Headers[header]);
+ }
+ }
+
+ return newRequest;
+ }
+
+ /// <summary>
+ /// Tests whether two arrays are equal in length and contents.
+ /// </summary>
+ /// <typeparam name="T">The type of elements in the arrays.</typeparam>
+ /// <param name="first">The first array in the comparison. May not be null.</param>
+ /// <param name="second">The second array in the comparison. May not be null.</param>
+ /// <returns>True if the arrays equal; false otherwise.</returns>
+ internal static bool AreEquivalent<T>(T[] first, T[] second) {
+ if (first == null) {
+ throw new ArgumentNullException("first");
+ }
+ if (second == null) {
+ throw new ArgumentNullException("second");
+ }
+ if (first.Length != second.Length) {
+ return false;
+ }
+ for (int i = 0; i < first.Length; i++) {
+ if (!first[i].Equals(second[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Tests whether two dictionaries are equal in length and contents.
+ /// </summary>
+ /// <typeparam name="TKey">The type of keys in the dictionaries.</typeparam>
+ /// <typeparam name="TValue">The type of values in the dictionaries.</typeparam>
+ /// <param name="first">The first dictionary in the comparison. May not be null.</param>
+ /// <param name="second">The second dictionary in the comparison. May not be null.</param>
+ /// <returns>True if the arrays equal; false otherwise.</returns>
+ internal static bool AreEquivalent<TKey, TValue>(IDictionary<TKey, TValue> first, IDictionary<TKey, TValue> second) {
+ return AreEquivalent(first.ToArray(), second.ToArray());
+ }
+
+ /// <summary>
+ /// Concatenates a list of name-value pairs as key=value&amp;key=value,
+ /// taking care to properly encode each key and value for URL
+ /// transmission. No ? is prefixed to the string.
+ /// </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) {
+ return string.Empty;
+ }
+ StringBuilder sb = new StringBuilder(args.Count * 10);
+
+ foreach (var p in args) {
+ sb.Append(HttpUtility.UrlEncode(p.Key));
+ sb.Append('=');
+ sb.Append(HttpUtility.UrlEncode(p.Value));
+ sb.Append('&');
+ }
+ sb.Length--; // remove trailing &
+
+ return sb.ToString();
+ }
+
+ /// <summary>
+ /// Adds a set of name-value pairs to the end of a given URL
+ /// as part of the querystring piece. Prefixes a ? or &amp; before
+ /// first element as necessary.
+ /// </summary>
+ /// <param name="builder">The UriBuilder to add arguments to.</param>
+ /// <param name="args">
+ /// 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) {
+ if (builder == null) {
+ throw new ArgumentNullException("builder");
+ }
+
+ 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('&');
+ }
+ sb.Append(CreateQueryString(args));
+
+ builder.Query = sb.ToString();
+ }
+ }
+
+ /// <summary>
+ /// Extracts the recipient from an HttpRequestInfo.
+ /// </summary>
+ /// <param name="request">The request to get recipient information from.</param>
+ /// <returns>The recipient.</returns>
+ internal static MessageReceivingEndpoint GetRecipient(this HttpRequestInfo request) {
+ return new MessageReceivingEndpoint(request.Url, request.HttpMethod == "GET" ? HttpDeliveryMethods.GetRequest : HttpDeliveryMethods.PostRequest);
+ }
+
+ /// <summary>
+ /// Copies some extra parameters into a message.
+ /// </summary>
+ /// <param name="message">The message to copy the extra data into.</param>
+ /// <param name="extraParameters">The extra data to copy into the message. May be null to do nothing.</param>
+ internal static void AddExtraParameters(this IMessage message, IDictionary<string, string> extraParameters) {
+ ErrorUtilities.VerifyArgumentNotNull(message, "message");
+
+ if (extraParameters != null) {
+ MessageDictionary messageDictionary = new MessageDictionary(message);
+ foreach (var pair in extraParameters) {
+ messageDictionary.Add(pair);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Converts a <see cref="NameValueCollection"/> to an IDictionary&lt;string, string&gt;.
+ /// </summary>
+ /// <param name="nvc">The NameValueCollection to convert. May be null.</param>
+ /// <returns>The generated dictionary, or null if <paramref name="nvc"/> is null.</returns>
+ internal static Dictionary<string, string> ToDictionary(this NameValueCollection nvc) {
+ if (nvc == null) {
+ return null;
+ }
+
+ var dictionary = new Dictionary<string, string>();
+ foreach (string key in nvc) {
+ dictionary.Add(key, nvc[key]);
+ }
+
+ return dictionary;
+ }
+
+ /// <summary>
+ /// Sorts the elements of a sequence in ascending order by using a specified comparer.
+ /// </summary>
+ /// <typeparam name="TSource">The type of the elements of source.</typeparam>
+ /// <typeparam name="TKey">The type of the key returned by keySelector.</typeparam>
+ /// <param name="source">A sequence of values to order.</param>
+ /// <param name="keySelector">A function to extract a key from an element.</param>
+ /// <param name="comparer">A comparison function to compare keys.</param>
+ /// <returns>An System.Linq.IOrderedEnumerable&lt;TElement&gt; whose elements are sorted according to a key.</returns>
+ internal static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Comparison<TKey> comparer) {
+ return System.Linq.Enumerable.OrderBy<TSource, TKey>(source, keySelector, new ComparisonHelper<TKey>(comparer));
+ }
+
+ /// <summary>
+ /// Determines whether the specified message is a request (indirect message or direct request).
+ /// </summary>
+ /// <param name="message">The message in question.</param>
+ /// <returns>
+ /// <c>true</c> if the specified message is a request; otherwise, <c>false</c>.
+ /// </returns>
+ /// <remarks>
+ /// Although an <see cref="IProtocolMessage"/> may implement the <see cref="IDirectedProtocolMessage"/>
+ /// interface, it may only be doing that for its derived classes. These objects are only requests
+ /// if their <see cref="IDirectedProtocolMessage.Recipient"/> property is non-null.
+ /// </remarks>
+ internal static bool IsRequest(this IDirectedProtocolMessage message) {
+ ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ return message.Recipient != null;
+ }
+
+ /// <summary>
+ /// Determines whether the specified message is a direct response.
+ /// </summary>
+ /// <param name="message">The message in question.</param>
+ /// <returns>
+ /// <c>true</c> if the specified message is a direct response; otherwise, <c>false</c>.
+ /// </returns>
+ /// <remarks>
+ /// Although an <see cref="IProtocolMessage"/> may implement the
+ /// <see cref="IDirectResponseProtocolMessage"/> interface, it may only be doing
+ /// that for its derived classes. These objects are only requests if their
+ /// <see cref="IDirectResponseProtocolMessage.OriginatingRequest"/> property is non-null.
+ /// </remarks>
+ internal static bool IsDirectResponse(this IDirectResponseProtocolMessage message) {
+ ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ return message.OriginatingRequest != null;
+ }
+
+ /// <summary>
+ /// A class to convert a <see cref="Comparison&lt;T&gt;"/> into an <see cref="IComparer&lt;T&gt;"/>.
+ /// </summary>
+ /// <typeparam name="T">The type of objects being compared.</typeparam>
+ private class ComparisonHelper<T> : IComparer<T> {
+ /// <summary>
+ /// The comparison method to use.
+ /// </summary>
+ private Comparison<T> comparison;
+
+ /// <summary>
+ /// Initializes a new instance of the ComparisonHelper class.
+ /// </summary>
+ /// <param name="comparison">The comparison method to use.</param>
+ internal ComparisonHelper(Comparison<T> comparison) {
+ if (comparison == null) {
+ throw new ArgumentNullException("comparison");
+ }
+
+ this.comparison = comparison;
+ }
+
+ #region IComparer<T> Members
+
+ /// <summary>
+ /// Compares two instances of <typeparamref name="T"/>.
+ /// </summary>
+ /// <param name="x">The first object to compare.</param>
+ /// <param name="y">The second object to compare.</param>
+ /// <returns>Any of -1, 0, or 1 according to standard comparison rules.</returns>
+ public int Compare(T x, T y) {
+ return this.comparison(x, y);
+ }
+
+ #endregion
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/ProtocolException.cs b/src/DotNetOpenAuth/Messaging/ProtocolException.cs
index 365a689..95c4940 100644
--- a/src/DotNetOpenAuth/Messaging/ProtocolException.cs
+++ b/src/DotNetOpenAuth/Messaging/ProtocolException.cs
@@ -1,289 +1,289 @@
-//-----------------------------------------------------------------------
-// <copyright file="ProtocolException.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging {
- using System;
- using System.Collections.Generic;
- using System.Security.Permissions;
-
- /// <summary>
- /// An exception to represent errors in the local or remote implementation of the protocol.
- /// </summary>
- [Serializable]
- public class ProtocolException : Exception, IDirectedProtocolMessage {
- /// <summary>
- /// The request message being processed when this exception was generated, if any.
- /// </summary>
- private IProtocolMessage inResponseTo;
-
- /// <summary>
- /// The indirect message receiver this exception should be delivered to, if any.
- /// </summary>
- private Uri recipient;
-
- /// <summary>
- /// A cache for extra name/value pairs tacked on as data when this exception is sent as a message.
- /// </summary>
- private Dictionary<string, string> extraData = new Dictionary<string, string>();
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ProtocolException"/> class.
- /// </summary>
- public ProtocolException() { }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ProtocolException"/> class.
- /// </summary>
- /// <param name="message">A message describing the specific error the occurred or was detected.</param>
- public ProtocolException(string message) : base(message) { }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ProtocolException"/> class.
- /// </summary>
- /// <param name="message">A message describing the specific error the occurred or was detected.</param>
- /// <param name="inner">The inner exception to include.</param>
- public ProtocolException(string message, Exception inner) : base(message, inner) { }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ProtocolException"/> class
- /// such that it can be sent as a protocol message response to a remote caller.
- /// </summary>
- /// <param name="message">The human-readable exception message.</param>
- /// <param name="faultedMessage">The message that was the cause of the exception. May not be null.</param>
- internal ProtocolException(string message, IProtocolMessage faultedMessage)
- : base(message) {
- if (faultedMessage == null) {
- throw new ArgumentNullException("faultedMessage");
- }
-
- this.FaultedMessage = faultedMessage;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ProtocolException"/> class
- /// such that it can be sent as a protocol message response to a remote caller.
- /// </summary>
- /// <param name="message">The human-readable exception message.</param>
- /// <param name="inResponseTo">
- /// If <paramref name="message"/> is a response to an incoming message, this is the incoming message.
- /// This is useful for error scenarios in deciding just how to send the response message.
- /// May be null.
- /// </param>
- /// <param name="remoteIndirectReceiver">
- /// In the case of exceptions that will be sent as indirect messages to the original calling
- /// remote party, this is the URI of that remote site's receiver.
- /// May be null only if the <paramref name="inResponseTo"/> message is a direct request.
- /// </param>
- internal ProtocolException(string message, IProtocolMessage inResponseTo, Uri remoteIndirectReceiver)
- : this(message) {
- if (inResponseTo == null) {
- throw new ArgumentNullException("inResponseTo");
- }
- this.inResponseTo = inResponseTo;
- this.FaultedMessage = inResponseTo;
-
- if (remoteIndirectReceiver == null && inResponseTo.Transport != MessageTransport.Direct) {
- // throw an exception, with ourselves as the inner exception (as fully initialized as we can be).
- throw new ArgumentNullException("remoteIndirectReceiver");
- }
- this.recipient = remoteIndirectReceiver;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ProtocolException"/> 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 ProtocolException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context)
- : base(info, context) {
- throw new NotImplementedException();
- }
-
- #region IProtocolMessage Properties
-
- /// <summary>
- /// Gets the version of the protocol this message is prepared to implement.
- /// </summary>
- Version IProtocolMessage.ProtocolVersion {
- get { return this.ProtocolVersion; }
- }
-
- /// <summary>
- /// Gets the level of protection this exception requires when transmitted as a message.
- /// </summary>
- MessageProtections IProtocolMessage.RequiredProtection {
- get { return RequiredProtection; }
- }
-
- /// <summary>
- /// Gets whether this is a direct or indirect message.
- /// </summary>
- MessageTransport IProtocolMessage.Transport {
- get { return this.Transport; }
- }
-
- /// <summary>
- /// Gets a value indicating whether this message was deserialized as an incoming message.
- /// </summary>
- /// <remarks>
- /// Always false because exceptions are not a valid message to deserialize.
- /// </remarks>
- bool IProtocolMessage.Incoming {
- get { return false; }
- }
-
- #endregion
-
- #region IDirectedProtocolMessage Members
-
- /// <summary>
- /// Gets the preferred method of transport for the message.
- /// </summary>
- /// <remarks>
- /// This exception may be delivered as an indirect message or a direct response. This property
- /// only applies to the former. The latter will never query this property.
- /// </remarks>
- HttpDeliveryMethods IDirectedProtocolMessage.HttpMethods {
- get { return HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.PostRequest; }
- }
-
- /// <summary>
- /// Gets the URL of the intended receiver of this message.
- /// </summary>
- /// <remarks>
- /// This property should only be called when the error is being sent as an indirect response.
- /// </remarks>
- Uri IDirectedProtocolMessage.Recipient {
- get { return this.Recipient; }
- }
-
- #endregion
-
- /// <summary>
- /// Gets the dictionary of additional name/value fields tacked on to this message.
- /// </summary>
- IDictionary<string, string> IProtocolMessage.ExtraData {
- get { return this.ExtraData; }
- }
-
- /// <summary>
- /// Gets the message that caused the exception.
- /// </summary>
- internal IProtocolMessage FaultedMessage {
- get;
- private set;
- }
-
- /// <summary>
- /// Gets the level of protection this exception requires when transmitted as a message.
- /// </summary>
- protected static MessageProtections RequiredProtection {
- get { return MessageProtections.None; }
- }
-
- /// <summary>
- /// Gets the preferred method of transport for the message.
- /// </summary>
- protected HttpDeliveryMethods HttpMethods {
- get { return ((IDirectedProtocolMessage)this).HttpMethods; }
- }
-
- /// <summary>
- /// Gets the URL of the intended receiver of this message.
- /// </summary>
- /// <remarks>
- /// This property should only be called when the error is being sent as an indirect response.
- /// </remarks>
- protected Uri Recipient {
- get {
- if (this.inResponseTo == null) {
- throw new InvalidOperationException(MessagingStrings.ExceptionNotConstructedForTransit);
- }
- return this.recipient;
- }
- }
-
- /// <summary>
- /// Gets the version of the protocol this message is prepared to implement.
- /// </summary>
- protected Version ProtocolVersion {
- get {
- if (this.inResponseTo == null) {
- throw new InvalidOperationException(MessagingStrings.ExceptionNotConstructedForTransit);
- }
- return this.inResponseTo.ProtocolVersion;
- }
- }
-
- /// <summary>
- /// Gets whether this is a direct or indirect message.
- /// </summary>
- protected MessageTransport Transport {
- get {
- if (this.inResponseTo == null) {
- throw new InvalidOperationException(MessagingStrings.ExceptionNotConstructedForTransit);
- }
- return this.inResponseTo.Transport;
- }
- }
-
- /// <summary>
- /// Gets the dictionary of additional name/value fields tacked on to this message.
- /// </summary>
- protected IDictionary<string, string> ExtraData {
- get { return this.extraData; }
- }
-
- /// <summary>
- /// Gets a value indicating whether this message was deserialized as an incoming message.
- /// </summary>
- protected bool Incoming {
- get { return false; }
- }
-
- /// <summary>
- /// When overridden in a derived class, sets the <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with information about the exception.
- /// </summary>
- /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
- /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.</param>
- /// <exception cref="T:System.ArgumentNullException">
- /// The <paramref name="info"/> parameter is a null reference (Nothing in Visual Basic).
- /// </exception>
- /// <PermissionSet>
- /// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Read="*AllFiles*" PathDiscovery="*AllFiles*"/>
- /// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="SerializationFormatter"/>
- /// </PermissionSet>
- [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
- public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) {
- base.GetObjectData(info, context);
- throw new NotImplementedException();
- }
-
- #region IProtocolMessage Methods
-
- /// <summary>
- /// See <see cref="IProtocolMessage.EnsureValidMessage"/>.
- /// </summary>
- void IProtocolMessage.EnsureValidMessage() {
- this.EnsureValidMessage();
- }
-
- /// <summary>
- /// See <see cref="IProtocolMessage.EnsureValidMessage"/>.
- /// </summary>
- protected virtual void EnsureValidMessage() {
- if (this.inResponseTo == null) {
- throw new InvalidOperationException(MessagingStrings.ExceptionNotConstructedForTransit);
- }
- }
-
- #endregion
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ProtocolException.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Security.Permissions;
+
+ /// <summary>
+ /// An exception to represent errors in the local or remote implementation of the protocol.
+ /// </summary>
+ [Serializable]
+ public class ProtocolException : Exception, IDirectedProtocolMessage {
+ /// <summary>
+ /// The request message being processed when this exception was generated, if any.
+ /// </summary>
+ private IProtocolMessage inResponseTo;
+
+ /// <summary>
+ /// The indirect message receiver this exception should be delivered to, if any.
+ /// </summary>
+ private Uri recipient;
+
+ /// <summary>
+ /// A cache for extra name/value pairs tacked on as data when this exception is sent as a message.
+ /// </summary>
+ private Dictionary<string, string> extraData = new Dictionary<string, string>();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ProtocolException"/> class.
+ /// </summary>
+ public ProtocolException() { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ProtocolException"/> class.
+ /// </summary>
+ /// <param name="message">A message describing the specific error the occurred or was detected.</param>
+ public ProtocolException(string message) : base(message) { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ProtocolException"/> class.
+ /// </summary>
+ /// <param name="message">A message describing the specific error the occurred or was detected.</param>
+ /// <param name="inner">The inner exception to include.</param>
+ public ProtocolException(string message, Exception inner) : base(message, inner) { }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ProtocolException"/> class
+ /// such that it can be sent as a protocol message response to a remote caller.
+ /// </summary>
+ /// <param name="message">The human-readable exception message.</param>
+ /// <param name="faultedMessage">The message that was the cause of the exception. May not be null.</param>
+ internal ProtocolException(string message, IProtocolMessage faultedMessage)
+ : base(message) {
+ if (faultedMessage == null) {
+ throw new ArgumentNullException("faultedMessage");
+ }
+
+ this.FaultedMessage = faultedMessage;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ProtocolException"/> class
+ /// such that it can be sent as a protocol message response to a remote caller.
+ /// </summary>
+ /// <param name="message">The human-readable exception message.</param>
+ /// <param name="inResponseTo">
+ /// If <paramref name="message"/> is a response to an incoming message, this is the incoming message.
+ /// This is useful for error scenarios in deciding just how to send the response message.
+ /// May be null.
+ /// </param>
+ /// <param name="remoteIndirectReceiver">
+ /// In the case of exceptions that will be sent as indirect messages to the original calling
+ /// remote party, this is the URI of that remote site's receiver.
+ /// May be null only if the <paramref name="inResponseTo"/> message is a direct request.
+ /// </param>
+ internal ProtocolException(string message, IProtocolMessage inResponseTo, Uri remoteIndirectReceiver)
+ : this(message) {
+ if (inResponseTo == null) {
+ throw new ArgumentNullException("inResponseTo");
+ }
+ this.inResponseTo = inResponseTo;
+ this.FaultedMessage = inResponseTo;
+
+ if (remoteIndirectReceiver == null && inResponseTo.Transport != MessageTransport.Direct) {
+ // throw an exception, with ourselves as the inner exception (as fully initialized as we can be).
+ throw new ArgumentNullException("remoteIndirectReceiver");
+ }
+ this.recipient = remoteIndirectReceiver;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ProtocolException"/> 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 ProtocolException(
+ System.Runtime.Serialization.SerializationInfo info,
+ System.Runtime.Serialization.StreamingContext context)
+ : base(info, context) {
+ throw new NotImplementedException();
+ }
+
+ #region IProtocolMessage Properties
+
+ /// <summary>
+ /// Gets the version of the protocol this message is prepared to implement.
+ /// </summary>
+ Version IMessage.Version {
+ get { return this.Version; }
+ }
+
+ /// <summary>
+ /// Gets the level of protection this exception requires when transmitted as a message.
+ /// </summary>
+ MessageProtections IProtocolMessage.RequiredProtection {
+ get { return RequiredProtection; }
+ }
+
+ /// <summary>
+ /// Gets whether this is a direct or indirect message.
+ /// </summary>
+ MessageTransport IProtocolMessage.Transport {
+ get { return this.Transport; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this message was deserialized as an incoming message.
+ /// </summary>
+ /// <remarks>
+ /// Always false because exceptions are not a valid message to deserialize.
+ /// </remarks>
+ bool IMessage.Incoming {
+ get { return false; }
+ }
+
+ #endregion
+
+ #region IDirectedProtocolMessage Members
+
+ /// <summary>
+ /// Gets the preferred method of transport for the message.
+ /// </summary>
+ /// <remarks>
+ /// This exception may be delivered as an indirect message or a direct response. This property
+ /// only applies to the former. The latter will never query this property.
+ /// </remarks>
+ HttpDeliveryMethods IDirectedProtocolMessage.HttpMethods {
+ get { return HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.PostRequest; }
+ }
+
+ /// <summary>
+ /// Gets the URL of the intended receiver of this message.
+ /// </summary>
+ /// <remarks>
+ /// This property should only be called when the error is being sent as an indirect response.
+ /// </remarks>
+ Uri IDirectedProtocolMessage.Recipient {
+ get { return this.Recipient; }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets the dictionary of additional name/value fields tacked on to this message.
+ /// </summary>
+ IDictionary<string, string> IMessage.ExtraData {
+ get { return this.ExtraData; }
+ }
+
+ /// <summary>
+ /// Gets the message that caused the exception.
+ /// </summary>
+ internal IProtocolMessage FaultedMessage {
+ get;
+ private set;
+ }
+
+ /// <summary>
+ /// Gets the level of protection this exception requires when transmitted as a message.
+ /// </summary>
+ protected static MessageProtections RequiredProtection {
+ get { return MessageProtections.None; }
+ }
+
+ /// <summary>
+ /// Gets the preferred method of transport for the message.
+ /// </summary>
+ protected HttpDeliveryMethods HttpMethods {
+ get { return ((IDirectedProtocolMessage)this).HttpMethods; }
+ }
+
+ /// <summary>
+ /// Gets the URL of the intended receiver of this message.
+ /// </summary>
+ /// <remarks>
+ /// This property should only be called when the error is being sent as an indirect response.
+ /// </remarks>
+ protected Uri Recipient {
+ get {
+ if (this.inResponseTo == null) {
+ throw new InvalidOperationException(MessagingStrings.ExceptionNotConstructedForTransit);
+ }
+ return this.recipient;
+ }
+ }
+
+ /// <summary>
+ /// Gets the version of the protocol this message is prepared to implement.
+ /// </summary>
+ protected Version Version {
+ get {
+ if (this.inResponseTo == null) {
+ throw new InvalidOperationException(MessagingStrings.ExceptionNotConstructedForTransit);
+ }
+ return this.inResponseTo.Version;
+ }
+ }
+
+ /// <summary>
+ /// Gets whether this is a direct or indirect message.
+ /// </summary>
+ protected MessageTransport Transport {
+ get {
+ if (this.inResponseTo == null) {
+ throw new InvalidOperationException(MessagingStrings.ExceptionNotConstructedForTransit);
+ }
+ return this.inResponseTo.Transport;
+ }
+ }
+
+ /// <summary>
+ /// Gets the dictionary of additional name/value fields tacked on to this message.
+ /// </summary>
+ protected IDictionary<string, string> ExtraData {
+ get { return this.extraData; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this message was deserialized as an incoming message.
+ /// </summary>
+ protected bool Incoming {
+ get { return false; }
+ }
+
+ /// <summary>
+ /// When overridden in a derived class, sets the <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with information about the exception.
+ /// </summary>
+ /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
+ /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.</param>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="info"/> parameter is a null reference (Nothing in Visual Basic).
+ /// </exception>
+ /// <PermissionSet>
+ /// <IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Read="*AllFiles*" PathDiscovery="*AllFiles*"/>
+ /// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="SerializationFormatter"/>
+ /// </PermissionSet>
+ [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
+ public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) {
+ base.GetObjectData(info, context);
+ throw new NotImplementedException();
+ }
+
+ #region IProtocolMessage Methods
+
+ /// <summary>
+ /// See <see cref="IMessage.EnsureValidMessage"/>.
+ /// </summary>
+ void IMessage.EnsureValidMessage() {
+ this.EnsureValidMessage();
+ }
+
+ /// <summary>
+ /// See <see cref="IMessage.EnsureValidMessage"/>.
+ /// </summary>
+ protected virtual void EnsureValidMessage() {
+ if (this.inResponseTo == null) {
+ throw new InvalidOperationException(MessagingStrings.ExceptionNotConstructedForTransit);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
index fa4ac83..e75d56a 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
@@ -1,321 +1,321 @@
-//-----------------------------------------------------------------------
-// <copyright file="MessageDictionary.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging.Reflection {
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Diagnostics;
-
- /// <summary>
- /// Wraps an <see cref="IProtocolMessage"/> instance in a dictionary that
- /// provides access to both well-defined message properties and "extra"
- /// name/value pairs that have no properties associated with them.
- /// </summary>
- internal class MessageDictionary : IDictionary<string, string> {
- /// <summary>
- /// The <see cref="IProtocolMessage"/> instance manipulated by this dictionary.
- /// </summary>
- private IProtocolMessage message;
-
- /// <summary>
- /// The <see cref="MessageDescription"/> instance that describes the message type.
- /// </summary>
- private MessageDescription description;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="MessageDictionary"/> class.
- /// </summary>
- /// <param name="message">The message instance whose values will be manipulated by this dictionary.</param>
- internal MessageDictionary(IProtocolMessage message) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
-
- this.message = message;
- this.description = MessageDescription.Get(message.GetType(), message.ProtocolVersion);
- }
-
- #region ICollection<KeyValuePair<string,string>> Properties
-
- /// <summary>
- /// Gets the number of explicitly set values in the message.
- /// </summary>
- public int Count {
- get { return this.Keys.Count; }
- }
-
- /// <summary>
- /// Gets a value indicating whether this message is read only.
- /// </summary>
- bool ICollection<KeyValuePair<string, string>>.IsReadOnly {
- get { return false; }
- }
-
- #endregion
-
- #region IDictionary<string,string> Properties
-
- /// <summary>
- /// Gets all the keys that have values associated with them.
- /// </summary>
- public ICollection<string> Keys {
- get {
- List<string> keys = new List<string>(this.message.ExtraData.Count + this.description.Mapping.Count);
- keys.AddRange(this.DeclaredKeys);
- keys.AddRange(this.AdditionalKeys);
- return keys.AsReadOnly();
- }
- }
-
- /// <summary>
- /// Gets the set of official OAuth keys that have non-null values associated with them.
- /// </summary>
- public ICollection<string> DeclaredKeys {
- get {
- List<string> keys = new List<string>(this.description.Mapping.Count);
- foreach (var pair in this.description.Mapping) {
- // Don't include keys with null values, but default values for structs is ok
- if (pair.Value.GetValue(this.message) != null) {
- keys.Add(pair.Key);
- }
- }
-
- return keys.AsReadOnly();
- }
- }
-
- /// <summary>
- /// Gets the keys that are in the message but not declared as official OAuth properties.
- /// </summary>
- public ICollection<string> AdditionalKeys {
- get { return this.message.ExtraData.Keys; }
- }
-
- /// <summary>
- /// Gets all the values.
- /// </summary>
- public ICollection<string> Values {
- get {
- List<string> values = new List<string>(this.message.ExtraData.Count + this.description.Mapping.Count);
- foreach (MessagePart part in this.description.Mapping.Values) {
- if (part.GetValue(this.message) != null) {
- values.Add(part.GetValue(this.message));
- }
- }
-
- foreach (string value in this.message.ExtraData.Values) {
- Debug.Assert(value != null, "Null values should never be allowed in the extra data dictionary.");
- values.Add(value);
- }
-
- return values.AsReadOnly();
- }
- }
-
- /// <summary>
- /// Gets or sets a value for some named value.
- /// </summary>
- /// <param name="key">The serialized form of a name for the value to read or write.</param>
- /// <returns>The named value.</returns>
- /// <remarks>
- /// If the key matches a declared property or field on the message type,
- /// that type member is set. Otherwise the key/value is stored in a
- /// dictionary for extra (weakly typed) strings.
- /// </remarks>
- /// <exception cref="ArgumentException">Thrown when setting a value that is not allowed for a given <paramref name="key"/>.</exception>
- public string this[string key] {
- get {
- MessagePart part;
- if (this.description.Mapping.TryGetValue(key, out part)) {
- // Never throw KeyNotFoundException for declared properties.
- return part.GetValue(this.message);
- } else {
- return this.message.ExtraData[key];
- }
- }
-
- set {
- MessagePart part;
- if (this.description.Mapping.TryGetValue(key, out part)) {
- part.SetValue(this.message, value);
- } else {
- if (value == null) {
- this.message.ExtraData.Remove(key);
- } else {
- this.message.ExtraData[key] = value;
- }
- }
- }
- }
-
- #endregion
-
- #region IDictionary<string,string> Methods
-
- /// <summary>
- /// Adds a named value to the message.
- /// </summary>
- /// <param name="key">The serialized form of the name whose value is being set.</param>
- /// <param name="value">The serialized form of the value.</param>
- /// <exception cref="ArgumentException">
- /// Thrown if <paramref name="key"/> already has a set value in this message.
- /// </exception>
- /// <exception cref="ArgumentNullException">
- /// Thrown if <paramref name="value"/> is null.
- /// </exception>
- public void Add(string key, string value) {
- if (value == null) {
- throw new ArgumentNullException("value");
- }
-
- MessagePart part;
- if (this.description.Mapping.TryGetValue(key, out part)) {
- if (part.IsNondefaultValueSet(this.message)) {
- throw new ArgumentException(MessagingStrings.KeyAlreadyExists);
- }
- part.SetValue(this.message, value);
- } else {
- this.message.ExtraData.Add(key, value);
- }
- }
-
- /// <summary>
- /// Checks whether some named parameter has a value set in the message.
- /// </summary>
- /// <param name="key">The serialized form of the message part's name.</param>
- /// <returns>True if the parameter by the given name has a set value. False otherwise.</returns>
- public bool ContainsKey(string key) {
- return this.message.ExtraData.ContainsKey(key) ||
- (this.description.Mapping.ContainsKey(key) && this.description.Mapping[key].GetValue(this.message) != null);
- }
-
- /// <summary>
- /// Removes a name and value from the message given its name.
- /// </summary>
- /// <param name="key">The serialized form of the name to remove.</param>
- /// <returns>True if a message part by the given name was found and removed. False otherwise.</returns>
- public bool Remove(string key) {
- if (this.message.ExtraData.Remove(key)) {
- return true;
- } else {
- MessagePart part;
- if (this.description.Mapping.TryGetValue(key, out part)) {
- if (part.GetValue(this.message) != null) {
- part.SetValue(this.message, null);
- return true;
- }
- }
- return false;
- }
- }
-
- /// <summary>
- /// Gets some named value if the key has a value.
- /// </summary>
- /// <param name="key">The name (in serialized form) of the value being sought.</param>
- /// <param name="value">The variable where the value will be set.</param>
- /// <returns>True if the key was found and <paramref name="value"/> was set. False otherwise.</returns>
- public bool TryGetValue(string key, out string value) {
- MessagePart part;
- if (this.description.Mapping.TryGetValue(key, out part)) {
- value = part.GetValue(this.message);
- return true;
- }
- return this.message.ExtraData.TryGetValue(key, out value);
- }
-
- #endregion
-
- #region ICollection<KeyValuePair<string,string>> Methods
-
- /// <summary>
- /// Sets a named value in the message.
- /// </summary>
- /// <param name="item">The name-value pair to add. The name is the serialized form of the key.</param>
- public void Add(KeyValuePair<string, string> item) {
- this.Add(item.Key, item.Value);
- }
-
- /// <summary>
- /// Removes all values in the message.
- /// </summary>
- public void Clear() {
- foreach (string key in this.Keys) {
- this.Remove(key);
- }
- }
-
- /// <summary>
- /// Checks whether a named value has been set on the message.
- /// </summary>
- /// <param name="item">The name/value pair.</param>
- /// <returns>True if the key exists and has the given value. False otherwise.</returns>
- public bool Contains(KeyValuePair<string, string> item) {
- MessagePart part;
- if (this.description.Mapping.TryGetValue(item.Key, out part)) {
- return string.Equals(part.GetValue(this.message), item.Value, StringComparison.Ordinal);
- } else {
- return this.message.ExtraData.Contains(item);
- }
- }
-
- /// <summary>
- /// Copies all the serializable data from the message to a key/value array.
- /// </summary>
- /// <param name="array">The array to copy to.</param>
- /// <param name="arrayIndex">The index in the <paramref name="array"/> to begin copying to.</param>
- void ICollection<KeyValuePair<string, string>>.CopyTo(KeyValuePair<string, string>[] array, int arrayIndex) {
- foreach (var pair in (IDictionary<string, string>)this) {
- array[arrayIndex++] = pair;
- }
- }
-
- /// <summary>
- /// Removes a named value from the message if it exists.
- /// </summary>
- /// <param name="item">The serialized form of the name and value to remove.</param>
- /// <returns>True if the name/value was found and removed. False otherwise.</returns>
- public bool Remove(KeyValuePair<string, string> item) {
- // We use contains because that checks that the value is equal as well.
- if (((ICollection<KeyValuePair<string, string>>)this).Contains(item)) {
- ((IDictionary<string, string>)this).Remove(item.Key);
- return true;
- }
- return false;
- }
-
- #endregion
-
- #region IEnumerable<KeyValuePair<string,string>> Members
-
- /// <summary>
- /// Gets an enumerator that generates KeyValuePair&lt;string, string&gt; instances
- /// for all the key/value pairs that are set in the message.
- /// </summary>
- /// <returns>The enumerator that can generate the name/value pairs.</returns>
- public IEnumerator<KeyValuePair<string, string>> GetEnumerator() {
- foreach (string key in this.Keys) {
- yield return new KeyValuePair<string, string>(key, this[key]);
- }
- }
-
- #endregion
-
- #region IEnumerable Members
-
- /// <summary>
- /// Gets an enumerator that generates KeyValuePair&lt;string, string&gt; instances
- /// for all the key/value pairs that are set in the message.
- /// </summary>
- /// <returns>The enumerator that can generate the name/value pairs.</returns>
- IEnumerator System.Collections.IEnumerable.GetEnumerator() {
- return ((IEnumerable<KeyValuePair<string, string>>)this).GetEnumerator();
- }
-
- #endregion
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="MessageDictionary.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging.Reflection {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+
+ /// <summary>
+ /// Wraps an <see cref="IMessage"/> instance in a dictionary that
+ /// provides access to both well-defined message properties and "extra"
+ /// name/value pairs that have no properties associated with them.
+ /// </summary>
+ internal class MessageDictionary : IDictionary<string, string> {
+ /// <summary>
+ /// The <see cref="IMessage"/> instance manipulated by this dictionary.
+ /// </summary>
+ private IMessage message;
+
+ /// <summary>
+ /// The <see cref="MessageDescription"/> instance that describes the message type.
+ /// </summary>
+ private MessageDescription description;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageDictionary"/> class.
+ /// </summary>
+ /// <param name="message">The message instance whose values will be manipulated by this dictionary.</param>
+ internal MessageDictionary(IMessage message) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ this.message = message;
+ this.description = MessageDescription.Get(message.GetType(), message.Version);
+ }
+
+ #region ICollection<KeyValuePair<string,string>> Properties
+
+ /// <summary>
+ /// Gets the number of explicitly set values in the message.
+ /// </summary>
+ public int Count {
+ get { return this.Keys.Count; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this message is read only.
+ /// </summary>
+ bool ICollection<KeyValuePair<string, string>>.IsReadOnly {
+ get { return false; }
+ }
+
+ #endregion
+
+ #region IDictionary<string,string> Properties
+
+ /// <summary>
+ /// Gets all the keys that have values associated with them.
+ /// </summary>
+ public ICollection<string> Keys {
+ get {
+ List<string> keys = new List<string>(this.message.ExtraData.Count + this.description.Mapping.Count);
+ keys.AddRange(this.DeclaredKeys);
+ keys.AddRange(this.AdditionalKeys);
+ return keys.AsReadOnly();
+ }
+ }
+
+ /// <summary>
+ /// Gets the set of official OAuth keys that have non-null values associated with them.
+ /// </summary>
+ public ICollection<string> DeclaredKeys {
+ get {
+ List<string> keys = new List<string>(this.description.Mapping.Count);
+ foreach (var pair in this.description.Mapping) {
+ // Don't include keys with null values, but default values for structs is ok
+ if (pair.Value.GetValue(this.message) != null) {
+ keys.Add(pair.Key);
+ }
+ }
+
+ return keys.AsReadOnly();
+ }
+ }
+
+ /// <summary>
+ /// Gets the keys that are in the message but not declared as official OAuth properties.
+ /// </summary>
+ public ICollection<string> AdditionalKeys {
+ get { return this.message.ExtraData.Keys; }
+ }
+
+ /// <summary>
+ /// Gets all the values.
+ /// </summary>
+ public ICollection<string> Values {
+ get {
+ List<string> values = new List<string>(this.message.ExtraData.Count + this.description.Mapping.Count);
+ foreach (MessagePart part in this.description.Mapping.Values) {
+ if (part.GetValue(this.message) != null) {
+ values.Add(part.GetValue(this.message));
+ }
+ }
+
+ foreach (string value in this.message.ExtraData.Values) {
+ Debug.Assert(value != null, "Null values should never be allowed in the extra data dictionary.");
+ values.Add(value);
+ }
+
+ return values.AsReadOnly();
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value for some named value.
+ /// </summary>
+ /// <param name="key">The serialized form of a name for the value to read or write.</param>
+ /// <returns>The named value.</returns>
+ /// <remarks>
+ /// If the key matches a declared property or field on the message type,
+ /// that type member is set. Otherwise the key/value is stored in a
+ /// dictionary for extra (weakly typed) strings.
+ /// </remarks>
+ /// <exception cref="ArgumentException">Thrown when setting a value that is not allowed for a given <paramref name="key"/>.</exception>
+ public string this[string key] {
+ get {
+ MessagePart part;
+ if (this.description.Mapping.TryGetValue(key, out part)) {
+ // Never throw KeyNotFoundException for declared properties.
+ return part.GetValue(this.message);
+ } else {
+ return this.message.ExtraData[key];
+ }
+ }
+
+ set {
+ MessagePart part;
+ if (this.description.Mapping.TryGetValue(key, out part)) {
+ part.SetValue(this.message, value);
+ } else {
+ if (value == null) {
+ this.message.ExtraData.Remove(key);
+ } else {
+ this.message.ExtraData[key] = value;
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region IDictionary<string,string> Methods
+
+ /// <summary>
+ /// Adds a named value to the message.
+ /// </summary>
+ /// <param name="key">The serialized form of the name whose value is being set.</param>
+ /// <param name="value">The serialized form of the value.</param>
+ /// <exception cref="ArgumentException">
+ /// Thrown if <paramref name="key"/> already has a set value in this message.
+ /// </exception>
+ /// <exception cref="ArgumentNullException">
+ /// Thrown if <paramref name="value"/> is null.
+ /// </exception>
+ public void Add(string key, string value) {
+ if (value == null) {
+ throw new ArgumentNullException("value");
+ }
+
+ MessagePart part;
+ if (this.description.Mapping.TryGetValue(key, out part)) {
+ if (part.IsNondefaultValueSet(this.message)) {
+ throw new ArgumentException(MessagingStrings.KeyAlreadyExists);
+ }
+ part.SetValue(this.message, value);
+ } else {
+ this.message.ExtraData.Add(key, value);
+ }
+ }
+
+ /// <summary>
+ /// Checks whether some named parameter has a value set in the message.
+ /// </summary>
+ /// <param name="key">The serialized form of the message part's name.</param>
+ /// <returns>True if the parameter by the given name has a set value. False otherwise.</returns>
+ public bool ContainsKey(string key) {
+ return this.message.ExtraData.ContainsKey(key) ||
+ (this.description.Mapping.ContainsKey(key) && this.description.Mapping[key].GetValue(this.message) != null);
+ }
+
+ /// <summary>
+ /// Removes a name and value from the message given its name.
+ /// </summary>
+ /// <param name="key">The serialized form of the name to remove.</param>
+ /// <returns>True if a message part by the given name was found and removed. False otherwise.</returns>
+ public bool Remove(string key) {
+ if (this.message.ExtraData.Remove(key)) {
+ return true;
+ } else {
+ MessagePart part;
+ if (this.description.Mapping.TryGetValue(key, out part)) {
+ if (part.GetValue(this.message) != null) {
+ part.SetValue(this.message, null);
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Gets some named value if the key has a value.
+ /// </summary>
+ /// <param name="key">The name (in serialized form) of the value being sought.</param>
+ /// <param name="value">The variable where the value will be set.</param>
+ /// <returns>True if the key was found and <paramref name="value"/> was set. False otherwise.</returns>
+ public bool TryGetValue(string key, out string value) {
+ MessagePart part;
+ if (this.description.Mapping.TryGetValue(key, out part)) {
+ value = part.GetValue(this.message);
+ return true;
+ }
+ return this.message.ExtraData.TryGetValue(key, out value);
+ }
+
+ #endregion
+
+ #region ICollection<KeyValuePair<string,string>> Methods
+
+ /// <summary>
+ /// Sets a named value in the message.
+ /// </summary>
+ /// <param name="item">The name-value pair to add. The name is the serialized form of the key.</param>
+ public void Add(KeyValuePair<string, string> item) {
+ this.Add(item.Key, item.Value);
+ }
+
+ /// <summary>
+ /// Removes all values in the message.
+ /// </summary>
+ public void Clear() {
+ foreach (string key in this.Keys) {
+ this.Remove(key);
+ }
+ }
+
+ /// <summary>
+ /// Checks whether a named value has been set on the message.
+ /// </summary>
+ /// <param name="item">The name/value pair.</param>
+ /// <returns>True if the key exists and has the given value. False otherwise.</returns>
+ public bool Contains(KeyValuePair<string, string> item) {
+ MessagePart part;
+ if (this.description.Mapping.TryGetValue(item.Key, out part)) {
+ return string.Equals(part.GetValue(this.message), item.Value, StringComparison.Ordinal);
+ } else {
+ return this.message.ExtraData.Contains(item);
+ }
+ }
+
+ /// <summary>
+ /// Copies all the serializable data from the message to a key/value array.
+ /// </summary>
+ /// <param name="array">The array to copy to.</param>
+ /// <param name="arrayIndex">The index in the <paramref name="array"/> to begin copying to.</param>
+ void ICollection<KeyValuePair<string, string>>.CopyTo(KeyValuePair<string, string>[] array, int arrayIndex) {
+ foreach (var pair in (IDictionary<string, string>)this) {
+ array[arrayIndex++] = pair;
+ }
+ }
+
+ /// <summary>
+ /// Removes a named value from the message if it exists.
+ /// </summary>
+ /// <param name="item">The serialized form of the name and value to remove.</param>
+ /// <returns>True if the name/value was found and removed. False otherwise.</returns>
+ public bool Remove(KeyValuePair<string, string> item) {
+ // We use contains because that checks that the value is equal as well.
+ if (((ICollection<KeyValuePair<string, string>>)this).Contains(item)) {
+ ((IDictionary<string, string>)this).Remove(item.Key);
+ return true;
+ }
+ return false;
+ }
+
+ #endregion
+
+ #region IEnumerable<KeyValuePair<string,string>> Members
+
+ /// <summary>
+ /// Gets an enumerator that generates KeyValuePair&lt;string, string&gt; instances
+ /// for all the key/value pairs that are set in the message.
+ /// </summary>
+ /// <returns>The enumerator that can generate the name/value pairs.</returns>
+ public IEnumerator<KeyValuePair<string, string>> GetEnumerator() {
+ foreach (string key in this.Keys) {
+ yield return new KeyValuePair<string, string>(key, this[key]);
+ }
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ /// <summary>
+ /// Gets an enumerator that generates KeyValuePair&lt;string, string&gt; instances
+ /// for all the key/value pairs that are set in the message.
+ /// </summary>
+ /// <returns>The enumerator that can generate the name/value pairs.</returns>
+ IEnumerator System.Collections.IEnumerable.GetEnumerator() {
+ return ((IEnumerable<KeyValuePair<string, string>>)this).GetEnumerator();
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
index 0799d16..4c47ebf 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
@@ -1,328 +1,328 @@
-//-----------------------------------------------------------------------
-// <copyright file="MessagePart.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.Messaging.Reflection {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.CodeAnalysis;
- using System.Globalization;
- using System.Net.Security;
- using System.Reflection;
- using System.Xml;
- using DotNetOpenAuth.OpenId;
-
- /// <summary>
- /// Describes an individual member of a message and assists in its serialization.
- /// </summary>
- internal class MessagePart {
- /// <summary>
- /// A map of converters that help serialize custom objects to string values and back again.
- /// </summary>
- private static readonly Dictionary<Type, ValueMapping> converters = new Dictionary<Type, ValueMapping>();
-
- /// <summary>
- /// A map of instantiated custom encoders used to encode/decode message parts.
- /// </summary>
- private static readonly Dictionary<Type, IMessagePartEncoder> encoders = new Dictionary<Type, IMessagePartEncoder>();
-
- /// <summary>
- /// The string-object conversion routines to use for this individual message part.
- /// </summary>
- private ValueMapping converter;
-
- /// <summary>
- /// The property that this message part is associated with, if aplicable.
- /// </summary>
- private PropertyInfo property;
-
- /// <summary>
- /// The field that this message part is associated with, if aplicable.
- /// </summary>
- private FieldInfo field;
-
- /// <summary>
- /// The type of the message part. (Not the type of the message itself).
- /// </summary>
- private Type memberDeclaredType;
-
- /// <summary>
- /// The default (uninitialized) value of the member inherent in its type.
- /// </summary>
- private object defaultMemberValue;
-
- /// <summary>
- /// Initializes static members of the <see cref="MessagePart"/> class.
- /// </summary>
- [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Much more efficient initialization when we can call methods.")]
- static MessagePart() {
- Map<Uri>(uri => uri.AbsoluteUri, str => new Uri(str));
- Map<DateTime>(dt => XmlConvert.ToString(dt, XmlDateTimeSerializationMode.Utc), str => XmlConvert.ToDateTime(str, XmlDateTimeSerializationMode.Utc));
- Map<byte[]>(bytes => Convert.ToBase64String(bytes), str => Convert.FromBase64String(str));
- Map<Realm>(realm => realm.ToString(), str => new Realm(str));
- Map<Identifier>(id => id.ToString(), str => Identifier.Parse(str));
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="MessagePart"/> class.
- /// </summary>
- /// <param name="member">
- /// A property or field of an <see cref="IProtocolMessage"/> implementing type
- /// that has a <see cref="MessagePartAttribute"/> attached to it.
- /// </param>
- /// <param name="attribute">
- /// The attribute discovered on <paramref name="member"/> that describes the
- /// serialization requirements of the message part.
- /// </param>
- internal MessagePart(MemberInfo member, MessagePartAttribute attribute) {
- if (member == null) {
- throw new ArgumentNullException("member");
- }
-
- this.field = member as FieldInfo;
- this.property = member as PropertyInfo;
- if (this.field == null && this.property == null) {
- throw new ArgumentException(
- string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.UnexpectedType,
- typeof(FieldInfo).Name + ", " + typeof(PropertyInfo).Name,
- member.GetType().Name),
- "member");
- }
-
- if (attribute == null) {
- throw new ArgumentNullException("attribute");
- }
-
- this.Name = attribute.Name ?? member.Name;
- this.RequiredProtection = attribute.RequiredProtection;
- this.IsRequired = attribute.IsRequired;
- this.AllowEmpty = attribute.AllowEmpty;
- this.memberDeclaredType = (this.field != null) ? this.field.FieldType : this.property.PropertyType;
- this.defaultMemberValue = DeriveDefaultValue(this.memberDeclaredType);
-
- if (attribute.Encoder == null) {
- if (!converters.TryGetValue(this.memberDeclaredType, out this.converter)) {
- this.converter = new ValueMapping(
- obj => obj != null ? obj.ToString() : null,
- str => str != null ? Convert.ChangeType(str, this.memberDeclaredType, CultureInfo.InvariantCulture) : null);
- }
- } else {
- var encoder = GetEncoder(attribute.Encoder);
- this.converter = new ValueMapping(
- obj => encoder.Encode(obj),
- str => encoder.Decode(str));
- }
-
- if (this.field != null && (this.field.Attributes & FieldAttributes.InitOnly) != 0) {
- this.IsConstantValue = true;
- } else if (this.property != null && !this.property.CanWrite) {
- this.IsConstantValue = true;
- }
-
- // Validate a sane combination of settings
- this.ValidateSettings();
- }
-
- /// <summary>
- /// Gets or sets the name to use when serializing or deserializing this parameter in a message.
- /// </summary>
- internal string Name { get; set; }
-
- /// <summary>
- /// Gets or sets whether this message part must be signed.
- /// </summary>
- internal ProtectionLevel RequiredProtection { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether this message part is required for the
- /// containing message to be valid.
- /// </summary>
- internal bool IsRequired { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether the string value is allowed to be empty in the serialized message.
- /// </summary>
- internal bool AllowEmpty { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether the field or property must remain its default value.
- /// </summary>
- internal bool IsConstantValue { get; set; }
-
- /// <summary>
- /// Sets the member of a given message to some given value.
- /// Used in deserialization.
- /// </summary>
- /// <param name="message">The message instance containing the member whose value should be set.</param>
- /// <param name="value">The string representation of the value to set.</param>
- internal void SetValue(IProtocolMessage message, string value) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
-
- try {
- if (this.IsConstantValue) {
- string constantValue = this.GetValue(message);
- if (!string.Equals(constantValue, value)) {
- throw new ArgumentException(string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.UnexpectedMessagePartValueForConstant,
- message.GetType().Name,
- this.Name,
- constantValue,
- value));
- }
- } else {
- if (this.property != null) {
- this.property.SetValue(message, this.ToValue(value), null);
- } else {
- this.field.SetValue(message, this.ToValue(value));
- }
- }
- } catch (FormatException ex) {
- throw ErrorUtilities.Wrap(ex, MessagingStrings.MessagePartReadFailure, message.GetType(), this.Name, value);
- }
- }
-
- /// <summary>
- /// Gets the value of a member of a given message.
- /// Used in serialization.
- /// </summary>
- /// <param name="message">The message instance to read the value from.</param>
- /// <returns>The string representation of the member's value.</returns>
- internal string GetValue(IProtocolMessage message) {
- try {
- object value = this.GetValueAsObject(message);
- return this.ToString(value);
- } catch (FormatException ex) {
- throw ErrorUtilities.Wrap(ex, MessagingStrings.MessagePartWriteFailure, message.GetType(), this.Name);
- }
- }
-
- /// <summary>
- /// Gets whether the value has been set to something other than its CLR type default value.
- /// </summary>
- /// <param name="message">The message instance to check the value on.</param>
- /// <returns>True if the value is not the CLR default value.</returns>
- internal bool IsNondefaultValueSet(IProtocolMessage message) {
- if (this.memberDeclaredType.IsValueType) {
- return !this.GetValueAsObject(message).Equals(this.defaultMemberValue);
- } else {
- return this.defaultMemberValue != this.GetValueAsObject(message);
- }
- }
-
- /// <summary>
- /// Figures out the CLR default value for a given type.
- /// </summary>
- /// <param name="type">The type whose default value is being sought.</param>
- /// <returns>Either null, or some default value like 0 or 0.0.</returns>
- private static object DeriveDefaultValue(Type type) {
- if (type.IsValueType) {
- return Activator.CreateInstance(type);
- } else {
- return null;
- }
- }
-
- /// <summary>
- /// Adds a pair of type conversion functions to the static converstion map.
- /// </summary>
- /// <typeparam name="T">The custom type to convert to and from strings.</typeparam>
- /// <param name="toString">The function to convert the custom type to a string.</param>
- /// <param name="toValue">The function to convert a string to the custom type.</param>
- private static void Map<T>(Func<T, string> toString, Func<string, T> toValue) {
- Func<object, string> safeToString = obj => obj != null ? toString((T)obj) : null;
- Func<string, object> safeToT = str => str != null ? toValue(str) : default(T);
- converters.Add(typeof(T), new ValueMapping(safeToString, safeToT));
- }
-
- /// <summary>
- /// Checks whether a type is a nullable value type (i.e. int?)
- /// </summary>
- /// <param name="type">The type in question.</param>
- /// <returns>True if this is a nullable value type.</returns>
- private static bool IsNonNullableValueType(Type type) {
- if (!type.IsValueType) {
- return false;
- }
-
- if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
- return false;
- }
-
- return true;
- }
-
- /// <summary>
- /// Retrieves a previously instantiated encoder of a given type, or creates a new one and stores it for later retrieval as well.
- /// </summary>
- /// <param name="messagePartEncoder">The message part encoder type.</param>
- /// <returns>An instance of the desired encoder.</returns>
- private static IMessagePartEncoder GetEncoder(Type messagePartEncoder) {
- IMessagePartEncoder encoder;
- if (!encoders.TryGetValue(messagePartEncoder, out encoder)) {
- encoder = encoders[messagePartEncoder] = (IMessagePartEncoder)Activator.CreateInstance(messagePartEncoder);
- }
-
- return encoder;
- }
-
- /// <summary>
- /// Converts a string representation of the member's value to the appropriate type.
- /// </summary>
- /// <param name="value">The string representation of the member's value.</param>
- /// <returns>
- /// An instance of the appropriate type for setting the member.
- /// </returns>
- private object ToValue(string value) {
- return value == null ? null : this.converter.StringToValue(value);
- }
-
- /// <summary>
- /// Converts the member's value to its string representation.
- /// </summary>
- /// <param name="value">The value of the member.</param>
- /// <returns>
- /// The string representation of the member's value.
- /// </returns>
- private string ToString(object value) {
- return value == null ? null : this.converter.ValueToString(value);
- }
-
- /// <summary>
- /// Gets the value of the message part, without converting it to/from a string.
- /// </summary>
- /// <param name="message">The message instance to read from.</param>
- /// <returns>The value of the member.</returns>
- private object GetValueAsObject(IProtocolMessage message) {
- if (this.property != null) {
- return this.property.GetValue(message, null);
- } else {
- return this.field.GetValue(message);
- }
- }
-
- /// <summary>
- /// Validates that the message part and its attribute have agreeable settings.
- /// </summary>
- /// <exception cref="ArgumentException">
- /// Thrown when a non-nullable value type is set as optional.
- /// </exception>
- private void ValidateSettings() {
- if (!this.IsRequired && IsNonNullableValueType(this.memberDeclaredType)) {
- MemberInfo member = (MemberInfo)this.field ?? this.property;
- throw new ArgumentException(
- string.Format(
- CultureInfo.CurrentCulture,
- "Invalid combination: {0} on message type {1} is a non-nullable value type but is marked as optional.",
- member.Name,
- member.DeclaringType));
- }
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="MessagePart.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging.Reflection {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
+ using System.Net.Security;
+ using System.Reflection;
+ using System.Xml;
+ using DotNetOpenAuth.OpenId;
+
+ /// <summary>
+ /// Describes an individual member of a message and assists in its serialization.
+ /// </summary>
+ internal class MessagePart {
+ /// <summary>
+ /// A map of converters that help serialize custom objects to string values and back again.
+ /// </summary>
+ private static readonly Dictionary<Type, ValueMapping> converters = new Dictionary<Type, ValueMapping>();
+
+ /// <summary>
+ /// A map of instantiated custom encoders used to encode/decode message parts.
+ /// </summary>
+ private static readonly Dictionary<Type, IMessagePartEncoder> encoders = new Dictionary<Type, IMessagePartEncoder>();
+
+ /// <summary>
+ /// The string-object conversion routines to use for this individual message part.
+ /// </summary>
+ private ValueMapping converter;
+
+ /// <summary>
+ /// The property that this message part is associated with, if aplicable.
+ /// </summary>
+ private PropertyInfo property;
+
+ /// <summary>
+ /// The field that this message part is associated with, if aplicable.
+ /// </summary>
+ private FieldInfo field;
+
+ /// <summary>
+ /// The type of the message part. (Not the type of the message itself).
+ /// </summary>
+ private Type memberDeclaredType;
+
+ /// <summary>
+ /// The default (uninitialized) value of the member inherent in its type.
+ /// </summary>
+ private object defaultMemberValue;
+
+ /// <summary>
+ /// Initializes static members of the <see cref="MessagePart"/> class.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Much more efficient initialization when we can call methods.")]
+ static MessagePart() {
+ Map<Uri>(uri => uri.AbsoluteUri, str => new Uri(str));
+ Map<DateTime>(dt => XmlConvert.ToString(dt, XmlDateTimeSerializationMode.Utc), str => XmlConvert.ToDateTime(str, XmlDateTimeSerializationMode.Utc));
+ Map<byte[]>(bytes => Convert.ToBase64String(bytes), str => Convert.FromBase64String(str));
+ Map<Realm>(realm => realm.ToString(), str => new Realm(str));
+ Map<Identifier>(id => id.ToString(), str => Identifier.Parse(str));
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessagePart"/> class.
+ /// </summary>
+ /// <param name="member">
+ /// A property or field of an <see cref="IMessage"/> implementing type
+ /// that has a <see cref="MessagePartAttribute"/> attached to it.
+ /// </param>
+ /// <param name="attribute">
+ /// The attribute discovered on <paramref name="member"/> that describes the
+ /// serialization requirements of the message part.
+ /// </param>
+ internal MessagePart(MemberInfo member, MessagePartAttribute attribute) {
+ if (member == null) {
+ throw new ArgumentNullException("member");
+ }
+
+ this.field = member as FieldInfo;
+ this.property = member as PropertyInfo;
+ if (this.field == null && this.property == null) {
+ throw new ArgumentException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.UnexpectedType,
+ typeof(FieldInfo).Name + ", " + typeof(PropertyInfo).Name,
+ member.GetType().Name),
+ "member");
+ }
+
+ if (attribute == null) {
+ throw new ArgumentNullException("attribute");
+ }
+
+ this.Name = attribute.Name ?? member.Name;
+ this.RequiredProtection = attribute.RequiredProtection;
+ this.IsRequired = attribute.IsRequired;
+ this.AllowEmpty = attribute.AllowEmpty;
+ this.memberDeclaredType = (this.field != null) ? this.field.FieldType : this.property.PropertyType;
+ this.defaultMemberValue = DeriveDefaultValue(this.memberDeclaredType);
+
+ if (attribute.Encoder == null) {
+ if (!converters.TryGetValue(this.memberDeclaredType, out this.converter)) {
+ this.converter = new ValueMapping(
+ obj => obj != null ? obj.ToString() : null,
+ str => str != null ? Convert.ChangeType(str, this.memberDeclaredType, CultureInfo.InvariantCulture) : null);
+ }
+ } else {
+ var encoder = GetEncoder(attribute.Encoder);
+ this.converter = new ValueMapping(
+ obj => encoder.Encode(obj),
+ str => encoder.Decode(str));
+ }
+
+ if (this.field != null && (this.field.Attributes & FieldAttributes.InitOnly) != 0) {
+ this.IsConstantValue = true;
+ } else if (this.property != null && !this.property.CanWrite) {
+ this.IsConstantValue = true;
+ }
+
+ // Validate a sane combination of settings
+ this.ValidateSettings();
+ }
+
+ /// <summary>
+ /// Gets or sets the name to use when serializing or deserializing this parameter in a message.
+ /// </summary>
+ internal string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets whether this message part must be signed.
+ /// </summary>
+ internal ProtectionLevel RequiredProtection { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether this message part is required for the
+ /// containing message to be valid.
+ /// </summary>
+ internal bool IsRequired { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the string value is allowed to be empty in the serialized message.
+ /// </summary>
+ internal bool AllowEmpty { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the field or property must remain its default value.
+ /// </summary>
+ internal bool IsConstantValue { get; set; }
+
+ /// <summary>
+ /// Sets the member of a given message to some given value.
+ /// Used in deserialization.
+ /// </summary>
+ /// <param name="message">The message instance containing the member whose value should be set.</param>
+ /// <param name="value">The string representation of the value to set.</param>
+ internal void SetValue(IMessage message, string value) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ try {
+ if (this.IsConstantValue) {
+ string constantValue = this.GetValue(message);
+ if (!string.Equals(constantValue, value)) {
+ throw new ArgumentException(string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.UnexpectedMessagePartValueForConstant,
+ message.GetType().Name,
+ this.Name,
+ constantValue,
+ value));
+ }
+ } else {
+ if (this.property != null) {
+ this.property.SetValue(message, this.ToValue(value), null);
+ } else {
+ this.field.SetValue(message, this.ToValue(value));
+ }
+ }
+ } catch (FormatException ex) {
+ throw ErrorUtilities.Wrap(ex, MessagingStrings.MessagePartReadFailure, message.GetType(), this.Name, value);
+ }
+ }
+
+ /// <summary>
+ /// Gets the value of a member of a given message.
+ /// Used in serialization.
+ /// </summary>
+ /// <param name="message">The message instance to read the value from.</param>
+ /// <returns>The string representation of the member's value.</returns>
+ internal string GetValue(IMessage message) {
+ try {
+ object value = this.GetValueAsObject(message);
+ return this.ToString(value);
+ } catch (FormatException ex) {
+ throw ErrorUtilities.Wrap(ex, MessagingStrings.MessagePartWriteFailure, message.GetType(), this.Name);
+ }
+ }
+
+ /// <summary>
+ /// Gets whether the value has been set to something other than its CLR type default value.
+ /// </summary>
+ /// <param name="message">The message instance to check the value on.</param>
+ /// <returns>True if the value is not the CLR default value.</returns>
+ internal bool IsNondefaultValueSet(IMessage message) {
+ if (this.memberDeclaredType.IsValueType) {
+ return !this.GetValueAsObject(message).Equals(this.defaultMemberValue);
+ } else {
+ return this.defaultMemberValue != this.GetValueAsObject(message);
+ }
+ }
+
+ /// <summary>
+ /// Figures out the CLR default value for a given type.
+ /// </summary>
+ /// <param name="type">The type whose default value is being sought.</param>
+ /// <returns>Either null, or some default value like 0 or 0.0.</returns>
+ private static object DeriveDefaultValue(Type type) {
+ if (type.IsValueType) {
+ return Activator.CreateInstance(type);
+ } else {
+ return null;
+ }
+ }
+
+ /// <summary>
+ /// Adds a pair of type conversion functions to the static converstion map.
+ /// </summary>
+ /// <typeparam name="T">The custom type to convert to and from strings.</typeparam>
+ /// <param name="toString">The function to convert the custom type to a string.</param>
+ /// <param name="toValue">The function to convert a string to the custom type.</param>
+ private static void Map<T>(Func<T, string> toString, Func<string, T> toValue) {
+ Func<object, string> safeToString = obj => obj != null ? toString((T)obj) : null;
+ Func<string, object> safeToT = str => str != null ? toValue(str) : default(T);
+ converters.Add(typeof(T), new ValueMapping(safeToString, safeToT));
+ }
+
+ /// <summary>
+ /// Checks whether a type is a nullable value type (i.e. int?)
+ /// </summary>
+ /// <param name="type">The type in question.</param>
+ /// <returns>True if this is a nullable value type.</returns>
+ private static bool IsNonNullableValueType(Type type) {
+ if (!type.IsValueType) {
+ return false;
+ }
+
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Retrieves a previously instantiated encoder of a given type, or creates a new one and stores it for later retrieval as well.
+ /// </summary>
+ /// <param name="messagePartEncoder">The message part encoder type.</param>
+ /// <returns>An instance of the desired encoder.</returns>
+ private static IMessagePartEncoder GetEncoder(Type messagePartEncoder) {
+ IMessagePartEncoder encoder;
+ if (!encoders.TryGetValue(messagePartEncoder, out encoder)) {
+ encoder = encoders[messagePartEncoder] = (IMessagePartEncoder)Activator.CreateInstance(messagePartEncoder);
+ }
+
+ return encoder;
+ }
+
+ /// <summary>
+ /// Converts a string representation of the member's value to the appropriate type.
+ /// </summary>
+ /// <param name="value">The string representation of the member's value.</param>
+ /// <returns>
+ /// An instance of the appropriate type for setting the member.
+ /// </returns>
+ private object ToValue(string value) {
+ return value == null ? null : this.converter.StringToValue(value);
+ }
+
+ /// <summary>
+ /// Converts the member's value to its string representation.
+ /// </summary>
+ /// <param name="value">The value of the member.</param>
+ /// <returns>
+ /// The string representation of the member's value.
+ /// </returns>
+ private string ToString(object value) {
+ return value == null ? null : this.converter.ValueToString(value);
+ }
+
+ /// <summary>
+ /// Gets the value of the message part, without converting it to/from a string.
+ /// </summary>
+ /// <param name="message">The message instance to read from.</param>
+ /// <returns>The value of the member.</returns>
+ private object GetValueAsObject(IMessage message) {
+ if (this.property != null) {
+ return this.property.GetValue(message, null);
+ } else {
+ return this.field.GetValue(message);
+ }
+ }
+
+ /// <summary>
+ /// Validates that the message part and its attribute have agreeable settings.
+ /// </summary>
+ /// <exception cref="ArgumentException">
+ /// Thrown when a non-nullable value type is set as optional.
+ /// </exception>
+ private void ValidateSettings() {
+ if (!this.IsRequired && IsNonNullableValueType(this.memberDeclaredType)) {
+ MemberInfo member = (MemberInfo)this.field ?? this.property;
+ throw new ArgumentException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ "Invalid combination: {0} on message type {1} is a non-nullable value type but is marked as optional.",
+ member.Name,
+ member.DeclaringType));
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs
index 9119aa9..a46205f 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs
@@ -261,7 +261,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// This method implements OAuth 1.0 section 5.2, item #1 (described in section 5.4).
/// </remarks>
private HttpWebRequest InitializeRequestAsAuthHeader(IDirectedProtocolMessage requestMessage) {
- var protocol = Protocol.Lookup(requestMessage.ProtocolVersion);
+ var protocol = Protocol.Lookup(requestMessage.Version);
var dictionary = new MessageDictionary(requestMessage);
// copy so as to not modify original
diff --git a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
index 90683e8..35f8768 100644
--- a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
+++ b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
@@ -1,166 +1,166 @@
-//-----------------------------------------------------------------------
-// <copyright file="ConsumerBase.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OAuth {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.CodeAnalysis;
- using System.Net;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Bindings;
- using DotNetOpenAuth.OAuth.ChannelElements;
- using DotNetOpenAuth.OAuth.Messages;
-
- /// <summary>
- /// Base class for <see cref="WebConsumer"/> and <see cref="DesktopConsumer"/> types.
- /// </summary>
- public class ConsumerBase {
- /// <summary>
- /// Initializes a new instance of the <see cref="ConsumerBase"/> class.
- /// </summary>
- /// <param name="serviceDescription">The endpoints and behavior of the Service Provider.</param>
- /// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param>
- protected ConsumerBase(ServiceProviderDescription serviceDescription, ITokenManager tokenManager) {
- if (serviceDescription == null) {
- throw new ArgumentNullException("serviceDescription");
- }
- if (tokenManager == null) {
- throw new ArgumentNullException("tokenManager");
- }
-
- ITamperProtectionChannelBindingElement signingElement = serviceDescription.CreateTamperProtectionElement();
- INonceStore store = new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge);
- this.OAuthChannel = new OAuthChannel(signingElement, store, tokenManager, new OAuthConsumerMessageFactory());
- this.ServiceProvider = serviceDescription;
- }
-
- /// <summary>
- /// Gets or sets the Consumer Key used to communicate with the Service Provider.
- /// </summary>
- public string ConsumerKey { get; set; }
-
- /// <summary>
- /// Gets the Service Provider that will be accessed.
- /// </summary>
- public ServiceProviderDescription ServiceProvider { get; private set; }
-
- /// <summary>
- /// Gets the persistence store for tokens and secrets.
- /// </summary>
- public ITokenManager TokenManager {
- get { return this.OAuthChannel.TokenManager; }
- }
-
- /// <summary>
- /// Gets the channel to use for sending/receiving messages.
- /// </summary>
- public Channel Channel {
- get { return this.OAuthChannel; }
- }
-
- /// <summary>
- /// Gets or sets the channel to use for sending/receiving messages.
- /// </summary>
- internal OAuthChannel OAuthChannel { get; set; }
-
- /// <summary>
- /// Creates a web request prepared with OAuth authorization
- /// that may be further tailored by adding parameters by the caller.
- /// </summary>
- /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
- /// <param name="accessToken">The access token that permits access to the protected resource.</param>
- /// <returns>The initialized WebRequest object.</returns>
- public WebRequest PrepareAuthorizedRequest(MessageReceivingEndpoint endpoint, string accessToken) {
- IDirectedProtocolMessage message = this.CreateAuthorizingMessage(endpoint, accessToken);
- HttpWebRequest wr = this.OAuthChannel.InitializeRequest(message);
- return wr;
- }
-
- /// <summary>
- /// Creates a web request prepared with OAuth authorization
- /// that may be further tailored by adding parameters by the caller.
- /// </summary>
- /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
- /// <param name="accessToken">The access token that permits access to the protected resource.</param>
- /// <returns>The initialized WebRequest object.</returns>
- /// <exception cref="WebException">Thrown if the request fails for any reason after it is sent to the Service Provider.</exception>
- public DirectWebResponse PrepareAuthorizedRequestAndSend(MessageReceivingEndpoint endpoint, string accessToken) {
- IDirectedProtocolMessage message = this.CreateAuthorizingMessage(endpoint, accessToken);
- HttpWebRequest wr = this.OAuthChannel.InitializeRequest(message);
- return this.Channel.WebRequestHandler.GetResponse(wr);
- }
-
- /// <summary>
- /// Prepares an OAuth message that begins an authorization request that will
- /// redirect the user to the Service Provider to provide that authorization.
- /// </summary>
- /// <param name="callback">
- /// An optional Consumer URL that the Service Provider should redirect the
- /// User Agent to upon successful authorization.
- /// </param>
- /// <param name="requestParameters">Extra parameters to add to the request token message. Optional.</param>
- /// <param name="redirectParameters">Extra parameters to add to the redirect to Service Provider message. Optional.</param>
- /// <param name="requestToken">The request token that must be exchanged for an access token after the user has provided authorization.</param>
- /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
- [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "3#", Justification = "Two results")]
- protected internal UserAuthorizationRequest PrepareRequestUserAuthorization(Uri callback, IDictionary<string, string> requestParameters, IDictionary<string, string> redirectParameters, out string requestToken) {
- // Obtain an unauthorized request token.
- var token = new UnauthorizedTokenRequest(this.ServiceProvider.RequestTokenEndpoint) {
- ConsumerKey = this.ConsumerKey,
- };
- token.AddNonOAuthParameters(requestParameters);
- var requestTokenResponse = this.Channel.Request<UnauthorizedTokenResponse>(token);
- this.TokenManager.StoreNewRequestToken(token, requestTokenResponse);
-
- // Request user authorization.
- ITokenContainingMessage assignedRequestToken = requestTokenResponse;
- var requestAuthorization = new UserAuthorizationRequest(this.ServiceProvider.UserAuthorizationEndpoint, assignedRequestToken.Token) {
- Callback = callback,
- };
- requestAuthorization.AddNonOAuthParameters(redirectParameters);
- requestToken = requestAuthorization.RequestToken;
- return requestAuthorization;
- }
-
- /// <summary>
- /// Creates a web request prepared with OAuth authorization
- /// that may be further tailored by adding parameters by the caller.
- /// </summary>
- /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
- /// <param name="accessToken">The access token that permits access to the protected resource.</param>
- /// <returns>The initialized WebRequest object.</returns>
- protected internal AccessProtectedResourceRequest CreateAuthorizingMessage(MessageReceivingEndpoint endpoint, string accessToken) {
- if (endpoint == null) {
- throw new ArgumentNullException("endpoint");
- }
- if (String.IsNullOrEmpty(accessToken)) {
- throw new ArgumentNullException("accessToken");
- }
-
- AccessProtectedResourceRequest message = new AccessProtectedResourceRequest(endpoint) {
- AccessToken = accessToken,
- ConsumerKey = this.ConsumerKey,
- };
-
- return message;
- }
-
- /// <summary>
- /// Exchanges a given request token for access token.
- /// </summary>
- /// <param name="requestToken">The request token that the user has authorized.</param>
- /// <returns>The access token assigned by the Service Provider.</returns>
- protected AuthorizedTokenResponse ProcessUserAuthorization(string requestToken) {
- var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint) {
- RequestToken = requestToken,
- ConsumerKey = this.ConsumerKey,
- };
- var grantAccess = this.Channel.Request<AuthorizedTokenResponse>(requestAccess);
- this.TokenManager.ExpireRequestTokenAndStoreNewAccessToken(this.ConsumerKey, requestToken, grantAccess.AccessToken, grantAccess.TokenSecret);
- return grantAccess;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="ConsumerBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Net;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.OAuth.ChannelElements;
+ using DotNetOpenAuth.OAuth.Messages;
+
+ /// <summary>
+ /// Base class for <see cref="WebConsumer"/> and <see cref="DesktopConsumer"/> types.
+ /// </summary>
+ public class ConsumerBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ConsumerBase"/> class.
+ /// </summary>
+ /// <param name="serviceDescription">The endpoints and behavior of the Service Provider.</param>
+ /// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param>
+ protected ConsumerBase(ServiceProviderDescription serviceDescription, ITokenManager tokenManager) {
+ if (serviceDescription == null) {
+ throw new ArgumentNullException("serviceDescription");
+ }
+ if (tokenManager == null) {
+ throw new ArgumentNullException("tokenManager");
+ }
+
+ ITamperProtectionChannelBindingElement signingElement = serviceDescription.CreateTamperProtectionElement();
+ INonceStore store = new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge);
+ this.OAuthChannel = new OAuthChannel(signingElement, store, tokenManager, new OAuthConsumerMessageFactory());
+ this.ServiceProvider = serviceDescription;
+ }
+
+ /// <summary>
+ /// Gets or sets the Consumer Key used to communicate with the Service Provider.
+ /// </summary>
+ public string ConsumerKey { get; set; }
+
+ /// <summary>
+ /// Gets the Service Provider that will be accessed.
+ /// </summary>
+ public ServiceProviderDescription ServiceProvider { get; private set; }
+
+ /// <summary>
+ /// Gets the persistence store for tokens and secrets.
+ /// </summary>
+ public ITokenManager TokenManager {
+ get { return this.OAuthChannel.TokenManager; }
+ }
+
+ /// <summary>
+ /// Gets the channel to use for sending/receiving messages.
+ /// </summary>
+ public Channel Channel {
+ get { return this.OAuthChannel; }
+ }
+
+ /// <summary>
+ /// Gets or sets the channel to use for sending/receiving messages.
+ /// </summary>
+ internal OAuthChannel OAuthChannel { get; set; }
+
+ /// <summary>
+ /// Creates a web request prepared with OAuth authorization
+ /// that may be further tailored by adding parameters by the caller.
+ /// </summary>
+ /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
+ /// <param name="accessToken">The access token that permits access to the protected resource.</param>
+ /// <returns>The initialized WebRequest object.</returns>
+ public WebRequest PrepareAuthorizedRequest(MessageReceivingEndpoint endpoint, string accessToken) {
+ IDirectedProtocolMessage message = this.CreateAuthorizingMessage(endpoint, accessToken);
+ HttpWebRequest wr = this.OAuthChannel.InitializeRequest(message);
+ return wr;
+ }
+
+ /// <summary>
+ /// Creates a web request prepared with OAuth authorization
+ /// that may be further tailored by adding parameters by the caller.
+ /// </summary>
+ /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
+ /// <param name="accessToken">The access token that permits access to the protected resource.</param>
+ /// <returns>The initialized WebRequest object.</returns>
+ /// <exception cref="WebException">Thrown if the request fails for any reason after it is sent to the Service Provider.</exception>
+ public DirectWebResponse PrepareAuthorizedRequestAndSend(MessageReceivingEndpoint endpoint, string accessToken) {
+ IDirectedProtocolMessage message = this.CreateAuthorizingMessage(endpoint, accessToken);
+ HttpWebRequest wr = this.OAuthChannel.InitializeRequest(message);
+ return this.Channel.WebRequestHandler.GetResponse(wr);
+ }
+
+ /// <summary>
+ /// Prepares an OAuth message that begins an authorization request that will
+ /// redirect the user to the Service Provider to provide that authorization.
+ /// </summary>
+ /// <param name="callback">
+ /// An optional Consumer URL that the Service Provider should redirect the
+ /// User Agent to upon successful authorization.
+ /// </param>
+ /// <param name="requestParameters">Extra parameters to add to the request token message. Optional.</param>
+ /// <param name="redirectParameters">Extra parameters to add to the redirect to Service Provider message. Optional.</param>
+ /// <param name="requestToken">The request token that must be exchanged for an access token after the user has provided authorization.</param>
+ /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
+ [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "3#", Justification = "Two results")]
+ protected internal UserAuthorizationRequest PrepareRequestUserAuthorization(Uri callback, IDictionary<string, string> requestParameters, IDictionary<string, string> redirectParameters, out string requestToken) {
+ // Obtain an unauthorized request token.
+ var token = new UnauthorizedTokenRequest(this.ServiceProvider.RequestTokenEndpoint) {
+ ConsumerKey = this.ConsumerKey,
+ };
+ token.AddExtraParameters(requestParameters);
+ var requestTokenResponse = this.Channel.Request<UnauthorizedTokenResponse>(token);
+ this.TokenManager.StoreNewRequestToken(token, requestTokenResponse);
+
+ // Request user authorization.
+ ITokenContainingMessage assignedRequestToken = requestTokenResponse;
+ var requestAuthorization = new UserAuthorizationRequest(this.ServiceProvider.UserAuthorizationEndpoint, assignedRequestToken.Token) {
+ Callback = callback,
+ };
+ requestAuthorization.AddExtraParameters(redirectParameters);
+ requestToken = requestAuthorization.RequestToken;
+ return requestAuthorization;
+ }
+
+ /// <summary>
+ /// Creates a web request prepared with OAuth authorization
+ /// that may be further tailored by adding parameters by the caller.
+ /// </summary>
+ /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
+ /// <param name="accessToken">The access token that permits access to the protected resource.</param>
+ /// <returns>The initialized WebRequest object.</returns>
+ protected internal AccessProtectedResourceRequest CreateAuthorizingMessage(MessageReceivingEndpoint endpoint, string accessToken) {
+ if (endpoint == null) {
+ throw new ArgumentNullException("endpoint");
+ }
+ if (String.IsNullOrEmpty(accessToken)) {
+ throw new ArgumentNullException("accessToken");
+ }
+
+ AccessProtectedResourceRequest message = new AccessProtectedResourceRequest(endpoint) {
+ AccessToken = accessToken,
+ ConsumerKey = this.ConsumerKey,
+ };
+
+ return message;
+ }
+
+ /// <summary>
+ /// Exchanges a given request token for access token.
+ /// </summary>
+ /// <param name="requestToken">The request token that the user has authorized.</param>
+ /// <returns>The access token assigned by the Service Provider.</returns>
+ protected AuthorizedTokenResponse ProcessUserAuthorization(string requestToken) {
+ var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint) {
+ RequestToken = requestToken,
+ ConsumerKey = this.ConsumerKey,
+ };
+ var grantAccess = this.Channel.Request<AuthorizedTokenResponse>(requestAccess);
+ this.TokenManager.ExpireRequestTokenAndStoreNewAccessToken(this.ConsumerKey, requestToken, grantAccess.AccessToken, grantAccess.TokenSecret);
+ return grantAccess;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs b/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs
index f835efd..5f9b733 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs
+++ b/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs
@@ -1,279 +1,279 @@
-//-----------------------------------------------------------------------
-// <copyright file="MessageBase.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OAuth.Messages {
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Reflection;
- using DotNetOpenAuth.OAuth.ChannelElements;
-
- /// <summary>
- /// A base class for all OAuth messages.
- /// </summary>
- public abstract class MessageBase : IDirectedProtocolMessage, IDirectResponseProtocolMessage {
- /// <summary>
- /// A store for extra name/value data pairs that are attached to this message.
- /// </summary>
- private Dictionary<string, string> extraData = new Dictionary<string, string>();
-
- /// <summary>
- /// Gets a value indicating whether signing this message is required.
- /// </summary>
- private MessageProtections protectionRequired;
-
- /// <summary>
- /// Gets a value indicating whether this is a direct or indirect message.
- /// </summary>
- private MessageTransport transport;
-
- /// <summary>
- /// The URI to the remote endpoint to send this message to.
- /// </summary>
- private MessageReceivingEndpoint recipient;
-
- /// <summary>
- /// Backing store for the <see cref="OriginatingRequest"/> properties.
- /// </summary>
- private IDirectedProtocolMessage originatingRequest;
-
- /// <summary>
- /// Backing store for the <see cref="Incoming"/> properties.
- /// </summary>
- private bool incoming;
-
-#if DEBUG
- /// <summary>
- /// Initializes static members of the <see cref="MessageBase"/> class.
- /// </summary>
- static MessageBase() {
- LowSecurityMode = true;
- }
-#endif
-
- /// <summary>
- /// Initializes a new instance of the <see cref="MessageBase"/> class for direct response messages.
- /// </summary>
- /// <param name="protectionRequired">The level of protection the message requires.</param>
- /// <param name="originatingRequest">The request that asked for this direct response.</param>
- protected MessageBase(MessageProtections protectionRequired, IDirectedProtocolMessage originatingRequest) {
- ErrorUtilities.VerifyArgumentNotNull(originatingRequest, "originatingRequest");
-
- this.protectionRequired = protectionRequired;
- this.transport = MessageTransport.Direct;
- this.originatingRequest = originatingRequest;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="MessageBase"/> class for direct requests or indirect messages.
- /// </summary>
- /// <param name="protectionRequired">The level of protection the message requires.</param>
- /// <param name="transport">A value indicating whether this message requires a direct or indirect transport.</param>
- /// <param name="recipient">The URI that a directed message will be delivered to.</param>
- protected MessageBase(MessageProtections protectionRequired, MessageTransport transport, MessageReceivingEndpoint recipient) {
- if (recipient == null) {
- throw new ArgumentNullException("recipient");
- }
-
- this.protectionRequired = protectionRequired;
- this.transport = transport;
- this.recipient = recipient;
- }
-
- #region IProtocolMessage Properties
-
- /// <summary>
- /// Gets the version of the protocol this message is prepared to implement.
- /// </summary>
- Version IProtocolMessage.ProtocolVersion {
- get { return this.ProtocolVersion; }
- }
-
- /// <summary>
- /// Gets the level of protection this message requires.
- /// </summary>
- MessageProtections IProtocolMessage.RequiredProtection {
- get { return this.RequiredProtection; }
- }
-
- /// <summary>
- /// Gets a value indicating whether this is a direct or indirect message.
- /// </summary>
- MessageTransport IProtocolMessage.Transport {
- get { return this.Transport; }
- }
-
- /// <summary>
- /// Gets the dictionary of additional name/value fields tacked on to this message.
- /// </summary>
- IDictionary<string, string> IProtocolMessage.ExtraData {
- get { return this.ExtraData; }
- }
-
- /// <summary>
- /// Gets a value indicating whether this message was deserialized as an incoming message.
- /// </summary>
- bool IProtocolMessage.Incoming {
- get { return this.incoming; }
- }
-
- #endregion
-
- #region IDirectedProtocolMessage Members
-
- /// <summary>
- /// Gets the URI to the Service Provider endpoint to send this message to.
- /// </summary>
- Uri IDirectedProtocolMessage.Recipient {
- get { return this.recipient != null ? this.recipient.Location : null; }
- }
-
- /// <summary>
- /// Gets the preferred method of transport for the message.
- /// </summary>
- HttpDeliveryMethods IDirectedProtocolMessage.HttpMethods {
- get { return this.HttpMethods; }
- }
-
- #endregion
-
- #region IDirectResponseProtocolMessage Members
-
- /// <summary>
- /// Gets the originating request message that caused this response to be formed.
- /// </summary>
- IDirectedProtocolMessage IDirectResponseProtocolMessage.OriginatingRequest {
- get { return this.originatingRequest; }
- }
-
- #endregion
-
- /// <summary>
- /// Gets or sets a value indicating whether security sensitive strings are
- /// emitted from the ToString() method.
- /// </summary>
- internal static bool LowSecurityMode { get; set; }
-
- /// <summary>
- /// Gets the version of the protocol this message is prepared to implement.
- /// </summary>
- protected virtual Version ProtocolVersion {
- get { return new Version(1, 0); }
- }
-
- /// <summary>
- /// Gets the level of protection this message requires.
- /// </summary>
- protected MessageProtections RequiredProtection {
- get { return this.protectionRequired; }
- }
-
- /// <summary>
- /// Gets a value indicating whether this is a direct or indirect message.
- /// </summary>
- protected MessageTransport Transport {
- get { return this.transport; }
- }
-
- /// <summary>
- /// Gets the dictionary of additional name/value fields tacked on to this message.
- /// </summary>
- protected IDictionary<string, string> ExtraData {
- get { return this.extraData; }
- }
-
- /// <summary>
- /// Gets a value indicating whether this message was deserialized as an incoming message.
- /// </summary>
- protected bool Incoming {
- get { return this.incoming; }
- }
-
- /// <summary>
- /// Gets the preferred method of transport for the message.
- /// </summary>
- protected HttpDeliveryMethods HttpMethods {
- get { return this.recipient != null ? this.recipient.AllowedMethods : HttpDeliveryMethods.None; }
- }
-
- /// <summary>
- /// Gets or sets the URI to the Service Provider endpoint to send this message to.
- /// </summary>
- protected Uri Recipient {
- get {
- return this.recipient != null ? this.recipient.Location : null;
- }
-
- set {
- if (this.recipient != null) {
- this.recipient = new MessageReceivingEndpoint(value, this.recipient.AllowedMethods);
- } else if (value != null) {
- throw new InvalidOperationException();
- }
- }
- }
-
- /// <summary>
- /// Gets the originating request message that caused this response to be formed.
- /// </summary>
- protected IDirectedProtocolMessage OriginatingRequest {
- get { return this.originatingRequest; }
- }
-
- #region IProtocolMessage Methods
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- void IProtocolMessage.EnsureValidMessage() {
- this.EnsureValidMessage();
- }
-
- #endregion
-
- /// <summary>
- /// Returns a human-friendly string describing the message and all serializable properties.
- /// </summary>
- /// <returns>The string representation of this object.</returns>
- public override string ToString() {
- StringBuilder builder = new StringBuilder();
- builder.AppendFormat(CultureInfo.InvariantCulture, "{0} message", GetType().Name);
- if (this.recipient != null) {
- builder.AppendFormat(CultureInfo.InvariantCulture, " as {0} to {1}", this.recipient.AllowedMethods, this.recipient.Location);
- }
- builder.AppendLine();
- MessageDictionary dictionary = new MessageDictionary(this);
- foreach (var pair in dictionary) {
- string value = pair.Value;
- if (pair.Key == "oauth_signature" && !LowSecurityMode) {
- value = "xxxxxxxxxxxxx (not shown)";
- }
- builder.Append('\t');
- builder.Append(pair.Key);
- builder.Append(": ");
- builder.AppendLine(value);
- }
-
- return builder.ToString();
- }
-
- /// <summary>
- /// Sets a flag indicating that this message is received (as opposed to sent).
- /// </summary>
- internal void SetAsIncoming() {
- this.incoming = true;
- }
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- protected virtual void EnsureValidMessage() { }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="MessageBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Reflection;
+ using DotNetOpenAuth.OAuth.ChannelElements;
+
+ /// <summary>
+ /// A base class for all OAuth messages.
+ /// </summary>
+ public abstract class MessageBase : IDirectedProtocolMessage, IDirectResponseProtocolMessage {
+ /// <summary>
+ /// A store for extra name/value data pairs that are attached to this message.
+ /// </summary>
+ private Dictionary<string, string> extraData = new Dictionary<string, string>();
+
+ /// <summary>
+ /// Gets a value indicating whether signing this message is required.
+ /// </summary>
+ private MessageProtections protectionRequired;
+
+ /// <summary>
+ /// Gets a value indicating whether this is a direct or indirect message.
+ /// </summary>
+ private MessageTransport transport;
+
+ /// <summary>
+ /// The URI to the remote endpoint to send this message to.
+ /// </summary>
+ private MessageReceivingEndpoint recipient;
+
+ /// <summary>
+ /// Backing store for the <see cref="OriginatingRequest"/> properties.
+ /// </summary>
+ private IDirectedProtocolMessage originatingRequest;
+
+ /// <summary>
+ /// Backing store for the <see cref="Incoming"/> properties.
+ /// </summary>
+ private bool incoming;
+
+#if DEBUG
+ /// <summary>
+ /// Initializes static members of the <see cref="MessageBase"/> class.
+ /// </summary>
+ static MessageBase() {
+ LowSecurityMode = true;
+ }
+#endif
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageBase"/> class for direct response messages.
+ /// </summary>
+ /// <param name="protectionRequired">The level of protection the message requires.</param>
+ /// <param name="originatingRequest">The request that asked for this direct response.</param>
+ protected MessageBase(MessageProtections protectionRequired, IDirectedProtocolMessage originatingRequest) {
+ ErrorUtilities.VerifyArgumentNotNull(originatingRequest, "originatingRequest");
+
+ this.protectionRequired = protectionRequired;
+ this.transport = MessageTransport.Direct;
+ this.originatingRequest = originatingRequest;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageBase"/> class for direct requests or indirect messages.
+ /// </summary>
+ /// <param name="protectionRequired">The level of protection the message requires.</param>
+ /// <param name="transport">A value indicating whether this message requires a direct or indirect transport.</param>
+ /// <param name="recipient">The URI that a directed message will be delivered to.</param>
+ protected MessageBase(MessageProtections protectionRequired, MessageTransport transport, MessageReceivingEndpoint recipient) {
+ if (recipient == null) {
+ throw new ArgumentNullException("recipient");
+ }
+
+ this.protectionRequired = protectionRequired;
+ this.transport = transport;
+ this.recipient = recipient;
+ }
+
+ #region IProtocolMessage Properties
+
+ /// <summary>
+ /// Gets the version of the protocol this message is prepared to implement.
+ /// </summary>
+ Version IMessage.Version {
+ get { return this.Version; }
+ }
+
+ /// <summary>
+ /// Gets the level of protection this message requires.
+ /// </summary>
+ MessageProtections IProtocolMessage.RequiredProtection {
+ get { return this.RequiredProtection; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this is a direct or indirect message.
+ /// </summary>
+ MessageTransport IProtocolMessage.Transport {
+ get { return this.Transport; }
+ }
+
+ /// <summary>
+ /// Gets the dictionary of additional name/value fields tacked on to this message.
+ /// </summary>
+ IDictionary<string, string> IMessage.ExtraData {
+ get { return this.ExtraData; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this message was deserialized as an incoming message.
+ /// </summary>
+ bool IMessage.Incoming {
+ get { return this.incoming; }
+ }
+
+ #endregion
+
+ #region IDirectedProtocolMessage Members
+
+ /// <summary>
+ /// Gets the URI to the Service Provider endpoint to send this message to.
+ /// </summary>
+ Uri IDirectedProtocolMessage.Recipient {
+ get { return this.recipient != null ? this.recipient.Location : null; }
+ }
+
+ /// <summary>
+ /// Gets the preferred method of transport for the message.
+ /// </summary>
+ HttpDeliveryMethods IDirectedProtocolMessage.HttpMethods {
+ get { return this.HttpMethods; }
+ }
+
+ #endregion
+
+ #region IDirectResponseProtocolMessage Members
+
+ /// <summary>
+ /// Gets the originating request message that caused this response to be formed.
+ /// </summary>
+ IDirectedProtocolMessage IDirectResponseProtocolMessage.OriginatingRequest {
+ get { return this.originatingRequest; }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets or sets a value indicating whether security sensitive strings are
+ /// emitted from the ToString() method.
+ /// </summary>
+ internal static bool LowSecurityMode { get; set; }
+
+ /// <summary>
+ /// Gets the version of the protocol this message is prepared to implement.
+ /// </summary>
+ protected virtual Version Version {
+ get { return new Version(1, 0); }
+ }
+
+ /// <summary>
+ /// Gets the level of protection this message requires.
+ /// </summary>
+ protected MessageProtections RequiredProtection {
+ get { return this.protectionRequired; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this is a direct or indirect message.
+ /// </summary>
+ protected MessageTransport Transport {
+ get { return this.transport; }
+ }
+
+ /// <summary>
+ /// Gets the dictionary of additional name/value fields tacked on to this message.
+ /// </summary>
+ protected IDictionary<string, string> ExtraData {
+ get { return this.extraData; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this message was deserialized as an incoming message.
+ /// </summary>
+ protected bool Incoming {
+ get { return this.incoming; }
+ }
+
+ /// <summary>
+ /// Gets the preferred method of transport for the message.
+ /// </summary>
+ protected HttpDeliveryMethods HttpMethods {
+ get { return this.recipient != null ? this.recipient.AllowedMethods : HttpDeliveryMethods.None; }
+ }
+
+ /// <summary>
+ /// Gets or sets the URI to the Service Provider endpoint to send this message to.
+ /// </summary>
+ protected Uri Recipient {
+ get {
+ return this.recipient != null ? this.recipient.Location : null;
+ }
+
+ set {
+ if (this.recipient != null) {
+ this.recipient = new MessageReceivingEndpoint(value, this.recipient.AllowedMethods);
+ } else if (value != null) {
+ throw new InvalidOperationException();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the originating request message that caused this response to be formed.
+ /// </summary>
+ protected IDirectedProtocolMessage OriginatingRequest {
+ get { return this.originatingRequest; }
+ }
+
+ #region IProtocolMessage Methods
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ void IMessage.EnsureValidMessage() {
+ this.EnsureValidMessage();
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Returns a human-friendly string describing the message and all serializable properties.
+ /// </summary>
+ /// <returns>The string representation of this object.</returns>
+ public override string ToString() {
+ StringBuilder builder = new StringBuilder();
+ builder.AppendFormat(CultureInfo.InvariantCulture, "{0} message", GetType().Name);
+ if (this.recipient != null) {
+ builder.AppendFormat(CultureInfo.InvariantCulture, " as {0} to {1}", this.recipient.AllowedMethods, this.recipient.Location);
+ }
+ builder.AppendLine();
+ MessageDictionary dictionary = new MessageDictionary(this);
+ foreach (var pair in dictionary) {
+ string value = pair.Value;
+ if (pair.Key == "oauth_signature" && !LowSecurityMode) {
+ value = "xxxxxxxxxxxxx (not shown)";
+ }
+ builder.Append('\t');
+ builder.Append(pair.Key);
+ builder.Append(": ");
+ builder.AppendLine(value);
+ }
+
+ return builder.ToString();
+ }
+
+ /// <summary>
+ /// Sets a flag indicating that this message is received (as opposed to sent).
+ /// </summary>
+ internal void SetAsIncoming() {
+ this.incoming = true;
+ }
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ protected virtual void EnsureValidMessage() { }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth/Messages/OAuth Messages.cd b/src/DotNetOpenAuth/OAuth/Messages/OAuth Messages.cd
index 6e1696e..5627be2 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/OAuth Messages.cd
+++ b/src/DotNetOpenAuth/OAuth/Messages/OAuth Messages.cd
@@ -1,215 +1,215 @@
-<?xml version="1.0" encoding="utf-8"?>
-<ClassDiagram MajorVersion="1" MinorVersion="1" GroupingSetting="Access">
- <Comment CommentText="Messages from Service Provider">
- <Position X="7.912" Y="0.715" Height="0.291" Width="2.02" />
- </Comment>
- <Comment CommentText="Messages from Consumer">
- <Position X="4.36" Y="0.683" Height="0.291" Width="2.02" />
- </Comment>
- <Class Name="DotNetOpenAuth.Messages.AccessProtectedResourceRequest">
- <Position X="4.25" Y="7.75" Width="3" />
- <Members>
- <Property Name="ITokenContainingMessage.Token" Hidden="true" />
- </Members>
- <InheritanceLine Type="DotNetOpenAuth.Messages.SignedMessageBase" ManuallyRouted="true" FixedFromPoint="true" FixedToPoint="true">
- <Path>
- <Point X="3.5" Y="5.626" />
- <Point X="3.5" Y="8.25" />
- <Point X="4.25" Y="8.25" />
- </Path>
- </InheritanceLine>
- <TypeIdentifier>
- <HashCode>AAAAAAACAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAA=</HashCode>
- <FileName>Messages\AccessProtectedResourceRequest.cs</FileName>
- </TypeIdentifier>
- <Lollipop Position="0.2" />
- </Class>
- <Class Name="DotNetOpenAuth.Messages.UnauthorizedTokenResponse">
- <Position X="7.5" Y="1.5" Width="3" />
- <Members>
- <Property Name="ITokenContainingMessage.Token" Hidden="true" />
- <Property Name="ITokenSecretContainingMessage.TokenSecret" Hidden="true" />
- </Members>
- <Compartments>
- <Compartment Name="Methods" Collapsed="true" />
- </Compartments>
- <TypeIdentifier>
- <HashCode>AAAAAAACAAAAAAAAAAEAAAAAAIAAIAAAIAAAAAAAAAA=</HashCode>
- <FileName>Messages\UnauthorizedTokenResponse.cs</FileName>
- </TypeIdentifier>
- <Lollipop Position="0.2" />
- </Class>
- <Class Name="DotNetOpenAuth.Messages.UserAuthorizationResponse">
- <Position X="7.5" Y="4.5" Width="3" />
- <Members>
- <Property Name="ITokenContainingMessage.Token" Hidden="true" />
- </Members>
- <Compartments>
- <Compartment Name="Methods" Collapsed="true" />
- </Compartments>
- <InheritanceLine Type="DotNetOpenAuth.Messages.MessageBase" FixedFromPoint="true" FixedToPoint="true">
- <Path>
- <Point X="10.75" Y="4.688" />
- <Point X="10.5" Y="4.688" />
- </Path>
- </InheritanceLine>
- <TypeIdentifier>
- <HashCode>AAAAAAACAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAA=</HashCode>
- <FileName>Messages\UserAuthorizationResponse.cs</FileName>
- </TypeIdentifier>
- <Lollipop Position="0.2" />
- </Class>
- <Class Name="DotNetOpenAuth.Messages.UserAuthorizationRequest">
- <Position X="4.25" Y="3" Width="3" />
- <Members>
- <Property Name="ITokenContainingMessage.Token" Hidden="true" />
- </Members>
- <Compartments>
- <Compartment Name="Methods" Collapsed="true" />
- </Compartments>
- <InheritanceLine Type="DotNetOpenAuth.Messages.MessageBase" FixedFromPoint="true" FixedToPoint="true">
- <Path>
- <Point X="10.75" Y="4.125" />
- <Point X="7.25" Y="4.125" />
- </Path>
- </InheritanceLine>
- <TypeIdentifier>
- <HashCode>AAAAAAACAAAAAAAAAAAAAAAAAIAAAAAAIAAAAAAAQAA=</HashCode>
- <FileName>Messages\UserAuthorizationRequest.cs</FileName>
- </TypeIdentifier>
- <Lollipop Position="0.2" />
- </Class>
- <Class Name="DotNetOpenAuth.Messages.AuthorizedTokenResponse">
- <Position X="7.5" Y="6.25" Width="3" />
- <Members>
- <Property Name="ITokenContainingMessage.Token" Hidden="true" />
- <Property Name="ITokenSecretContainingMessage.TokenSecret" Hidden="true" />
- </Members>
- <Compartments>
- <Compartment Name="Methods" Collapsed="true" />
- </Compartments>
- <InheritanceLine Type="DotNetOpenAuth.Messages.MessageBase" ManuallyRouted="true" FixedFromPoint="true" FixedToPoint="true">
- <Path>
- <Point X="11" Y="4.805" />
- <Point X="11" Y="7.013" />
- <Point X="10.5" Y="7.013" />
- </Path>
- </InheritanceLine>
- <TypeIdentifier>
- <HashCode>AAAAAAACAAAAAAAAAAAAAAAAEAAAIAAAIAAAAAAAAAA=</HashCode>
- <FileName>Messages\AuthorizedTokenResponse.cs</FileName>
- </TypeIdentifier>
- <Lollipop Position="0.2" />
- </Class>
- <Class Name="DotNetOpenAuth.Messages.MessageBase">
- <Position X="10.75" Y="1" Width="3.5" />
- <Members>
- <Field Name="extraData" Hidden="true" />
- <Property Name="IDirectedProtocolMessage.Recipient" Hidden="true" />
- <Property Name="IOAuthDirectedMessage.HttpMethods" Hidden="true" />
- <Property Name="IOAuthDirectedMessage.Recipient" Hidden="true" />
- <Method Name="IProtocolMessage.EnsureValidMessage" Hidden="true" />
- <Property Name="IProtocolMessage.ExtraData" Hidden="true" />
- <Property Name="IProtocolMessage.ProtocolVersion" Hidden="true" />
- <Property Name="IProtocolMessage.RequiredProtection" Hidden="true" />
- <Property Name="IProtocolMessage.Transport" Hidden="true" />
- <Field Name="protectionRequired" Hidden="true" />
- <Field Name="recipient" Hidden="true" />
- <Field Name="transport" Hidden="true" />
- </Members>
- <Compartments>
- <Compartment Name="Fields" Collapsed="true" />
- </Compartments>
- <TypeIdentifier>
- <HashCode>AAAKAAAAYAAAgAEEAIAAAAYAAAQEIDAAIgCCACAAAAA=</HashCode>
- <FileName>Messages\MessageBase.cs</FileName>
- </TypeIdentifier>
- <Lollipop Position="0.2" />
- </Class>
- <Class Name="DotNetOpenAuth.Messages.AuthorizedTokenRequest">
- <Position X="4.25" Y="5.5" Width="3" />
- <Members>
- <Property Name="ITokenContainingMessage.Token" Hidden="true" />
- </Members>
- <Compartments>
- <Compartment Name="Methods" Collapsed="true" />
- </Compartments>
- <InheritanceLine Type="DotNetOpenAuth.Messages.SignedMessageBase" ManuallyRouted="true" FixedFromPoint="true" FixedToPoint="true">
- <Path>
- <Point X="4" Y="4.947" />
- <Point X="4.123" Y="4.947" />
- <Point X="4.123" Y="5.75" />
- <Point X="4.25" Y="5.75" />
- </Path>
- </InheritanceLine>
- <TypeIdentifier>
- <HashCode>AAAAAAACQAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAA=</HashCode>
- <FileName>Messages\AuthorizedTokenRequest.cs</FileName>
- </TypeIdentifier>
- <Lollipop Position="0.2" />
- </Class>
- <Class Name="DotNetOpenAuth.Messages.UnauthorizedTokenRequest">
- <Position X="4.25" Y="1" Width="3" />
- <Compartments>
- <Compartment Name="Internal" Collapsed="true" />
- <Compartment Name="Private" Collapsed="true" />
- <Compartment Name="Methods" Collapsed="true" />
- </Compartments>
- <InheritanceLine Type="DotNetOpenAuth.Messages.SignedMessageBase" FixedFromPoint="true">
- <Path>
- <Point X="4" Y="1.855" />
- <Point X="4.25" Y="1.855" />
- </Path>
- </InheritanceLine>
- <TypeIdentifier>
- <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAA=</HashCode>
- <FileName>Messages\UnauthorizedTokenRequest.cs</FileName>
- </TypeIdentifier>
- </Class>
- <Class Name="DotNetOpenAuth.Messages.SignedMessageBase">
- <Position X="0.5" Y="1.5" Width="3.5" />
- <Members>
- <Property Name="ITamperResistantOAuthMessage.ConsumerSecret" Hidden="true" />
- <Property Name="ITamperResistantOAuthMessage.HttpMethod" Hidden="true" />
- <Property Name="ITamperResistantOAuthMessage.SignatureMethod" Hidden="true" />
- <Property Name="ITamperResistantOAuthMessage.TokenSecret" Hidden="true" />
- <Property Name="ITamperResistantProtocolMessage.Signature" Hidden="true" />
- </Members>
- <InheritanceLine Type="DotNetOpenAuth.Messages.MessageBase" FixedFromPoint="true" FixedToPoint="true">
- <Path>
- <Point X="12.875" Y="1" />
- <Point X="12.875" Y="0.625" />
- <Point X="3" Y="0.625" />
- <Point X="3" Y="1.5" />
- </Path>
- </InheritanceLine>
- <TypeIdentifier>
- <HashCode>IIAAFAAAAIAAAAAAgICAAgAAAgAAIAQAAAEAIAAQAAE=</HashCode>
- <FileName>Messages\SignedMessageBase.cs</FileName>
- </TypeIdentifier>
- <Lollipop Position="0.2" />
- </Class>
- <Interface Name="DotNetOpenAuth.ChannelElements.ITamperResistantOAuthMessage">
- <Position X="11.25" Y="5.25" Width="2.5" />
- <TypeIdentifier>
- <HashCode>AIAAAAAAAAAAAAAAAIAAAgAAAAAAIAQAAAAAAAAAAAA=</HashCode>
- <FileName>ChannelElements\ITamperResistantOAuthMessage.cs</FileName>
- </TypeIdentifier>
- </Interface>
- <Interface Name="DotNetOpenAuth.Messages.ITokenSecretContainingMessage">
- <Position X="1" Y="7.5" Width="2" />
- <TypeIdentifier>
- <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAA=</HashCode>
- <FileName>Messages\ITokenSecretContainingMessage.cs</FileName>
- <NewMemberFileName>Messages\ITokenContainingMessage.cs</NewMemberFileName>
- </TypeIdentifier>
- </Interface>
- <Interface Name="DotNetOpenAuth.Messages.ITokenContainingMessage">
- <Position X="1" Y="6" Width="2" />
- <TypeIdentifier>
- <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAA=</HashCode>
- <FileName>Messages\ITokenContainingMessage.cs</FileName>
- </TypeIdentifier>
- </Interface>
- <Font Name="Segoe UI" Size="9" />
+<?xml version="1.0" encoding="utf-8"?>
+<ClassDiagram MajorVersion="1" MinorVersion="1" GroupingSetting="Access">
+ <Comment CommentText="Messages from Service Provider">
+ <Position X="7.912" Y="0.715" Height="0.291" Width="2.02" />
+ </Comment>
+ <Comment CommentText="Messages from Consumer">
+ <Position X="4.36" Y="0.683" Height="0.291" Width="2.02" />
+ </Comment>
+ <Class Name="DotNetOpenAuth.Messages.AccessProtectedResourceRequest">
+ <Position X="4.25" Y="7.75" Width="3" />
+ <Members>
+ <Property Name="ITokenContainingMessage.Token" Hidden="true" />
+ </Members>
+ <InheritanceLine Type="DotNetOpenAuth.Messages.SignedMessageBase" ManuallyRouted="true" FixedFromPoint="true" FixedToPoint="true">
+ <Path>
+ <Point X="3.5" Y="5.626" />
+ <Point X="3.5" Y="8.25" />
+ <Point X="4.25" Y="8.25" />
+ </Path>
+ </InheritanceLine>
+ <TypeIdentifier>
+ <HashCode>AAAAAAACAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAA=</HashCode>
+ <FileName>Messages\AccessProtectedResourceRequest.cs</FileName>
+ </TypeIdentifier>
+ <Lollipop Position="0.2" />
+ </Class>
+ <Class Name="DotNetOpenAuth.Messages.UnauthorizedTokenResponse">
+ <Position X="7.5" Y="1.5" Width="3" />
+ <Members>
+ <Property Name="ITokenContainingMessage.Token" Hidden="true" />
+ <Property Name="ITokenSecretContainingMessage.TokenSecret" Hidden="true" />
+ </Members>
+ <Compartments>
+ <Compartment Name="Methods" Collapsed="true" />
+ </Compartments>
+ <TypeIdentifier>
+ <HashCode>AAAAAAACAAAAAAAAAAEAAAAAAIAAIAAAIAAAAAAAAAA=</HashCode>
+ <FileName>Messages\UnauthorizedTokenResponse.cs</FileName>
+ </TypeIdentifier>
+ <Lollipop Position="0.2" />
+ </Class>
+ <Class Name="DotNetOpenAuth.Messages.UserAuthorizationResponse">
+ <Position X="7.5" Y="4.5" Width="3" />
+ <Members>
+ <Property Name="ITokenContainingMessage.Token" Hidden="true" />
+ </Members>
+ <Compartments>
+ <Compartment Name="Methods" Collapsed="true" />
+ </Compartments>
+ <InheritanceLine Type="DotNetOpenAuth.Messages.MessageBase" FixedFromPoint="true" FixedToPoint="true">
+ <Path>
+ <Point X="10.75" Y="4.688" />
+ <Point X="10.5" Y="4.688" />
+ </Path>
+ </InheritanceLine>
+ <TypeIdentifier>
+ <HashCode>AAAAAAACAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAA=</HashCode>
+ <FileName>Messages\UserAuthorizationResponse.cs</FileName>
+ </TypeIdentifier>
+ <Lollipop Position="0.2" />
+ </Class>
+ <Class Name="DotNetOpenAuth.Messages.UserAuthorizationRequest">
+ <Position X="4.25" Y="3" Width="3" />
+ <Members>
+ <Property Name="ITokenContainingMessage.Token" Hidden="true" />
+ </Members>
+ <Compartments>
+ <Compartment Name="Methods" Collapsed="true" />
+ </Compartments>
+ <InheritanceLine Type="DotNetOpenAuth.Messages.MessageBase" FixedFromPoint="true" FixedToPoint="true">
+ <Path>
+ <Point X="10.75" Y="4.125" />
+ <Point X="7.25" Y="4.125" />
+ </Path>
+ </InheritanceLine>
+ <TypeIdentifier>
+ <HashCode>AAAAAAACAAAAAAAAAAAAAAAAAIAAAAAAIAAAAAAAQAA=</HashCode>
+ <FileName>Messages\UserAuthorizationRequest.cs</FileName>
+ </TypeIdentifier>
+ <Lollipop Position="0.2" />
+ </Class>
+ <Class Name="DotNetOpenAuth.Messages.AuthorizedTokenResponse">
+ <Position X="7.5" Y="6.25" Width="3" />
+ <Members>
+ <Property Name="ITokenContainingMessage.Token" Hidden="true" />
+ <Property Name="ITokenSecretContainingMessage.TokenSecret" Hidden="true" />
+ </Members>
+ <Compartments>
+ <Compartment Name="Methods" Collapsed="true" />
+ </Compartments>
+ <InheritanceLine Type="DotNetOpenAuth.Messages.MessageBase" ManuallyRouted="true" FixedFromPoint="true" FixedToPoint="true">
+ <Path>
+ <Point X="11" Y="4.805" />
+ <Point X="11" Y="7.013" />
+ <Point X="10.5" Y="7.013" />
+ </Path>
+ </InheritanceLine>
+ <TypeIdentifier>
+ <HashCode>AAAAAAACAAAAAAAAAAAAAAAAEAAAIAAAIAAAAAAAAAA=</HashCode>
+ <FileName>Messages\AuthorizedTokenResponse.cs</FileName>
+ </TypeIdentifier>
+ <Lollipop Position="0.2" />
+ </Class>
+ <Class Name="DotNetOpenAuth.Messages.MessageBase">
+ <Position X="10.75" Y="1" Width="3.5" />
+ <Members>
+ <Field Name="extraData" Hidden="true" />
+ <Property Name="IDirectedProtocolMessage.Recipient" Hidden="true" />
+ <Property Name="IOAuthDirectedMessage.HttpMethods" Hidden="true" />
+ <Property Name="IOAuthDirectedMessage.Recipient" Hidden="true" />
+ <Method Name="IMessage.EnsureValidMessage" Hidden="true" />
+ <Property Name="IMessage.ExtraData" Hidden="true" />
+ <Property Name="IMessage.Version" Hidden="true" />
+ <Property Name="IProtocolMessage.RequiredProtection" Hidden="true" />
+ <Property Name="IProtocolMessage.Transport" Hidden="true" />
+ <Field Name="protectionRequired" Hidden="true" />
+ <Field Name="recipient" Hidden="true" />
+ <Field Name="transport" Hidden="true" />
+ </Members>
+ <Compartments>
+ <Compartment Name="Fields" Collapsed="true" />
+ </Compartments>
+ <TypeIdentifier>
+ <HashCode>AAAKAAAAYAAAgAEEAIAAAAYAAAQEIDAAIgCCACAAAAA=</HashCode>
+ <FileName>Messages\MessageBase.cs</FileName>
+ </TypeIdentifier>
+ <Lollipop Position="0.2" />
+ </Class>
+ <Class Name="DotNetOpenAuth.Messages.AuthorizedTokenRequest">
+ <Position X="4.25" Y="5.5" Width="3" />
+ <Members>
+ <Property Name="ITokenContainingMessage.Token" Hidden="true" />
+ </Members>
+ <Compartments>
+ <Compartment Name="Methods" Collapsed="true" />
+ </Compartments>
+ <InheritanceLine Type="DotNetOpenAuth.Messages.SignedMessageBase" ManuallyRouted="true" FixedFromPoint="true" FixedToPoint="true">
+ <Path>
+ <Point X="4" Y="4.947" />
+ <Point X="4.123" Y="4.947" />
+ <Point X="4.123" Y="5.75" />
+ <Point X="4.25" Y="5.75" />
+ </Path>
+ </InheritanceLine>
+ <TypeIdentifier>
+ <HashCode>AAAAAAACQAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAA=</HashCode>
+ <FileName>Messages\AuthorizedTokenRequest.cs</FileName>
+ </TypeIdentifier>
+ <Lollipop Position="0.2" />
+ </Class>
+ <Class Name="DotNetOpenAuth.Messages.UnauthorizedTokenRequest">
+ <Position X="4.25" Y="1" Width="3" />
+ <Compartments>
+ <Compartment Name="Internal" Collapsed="true" />
+ <Compartment Name="Private" Collapsed="true" />
+ <Compartment Name="Methods" Collapsed="true" />
+ </Compartments>
+ <InheritanceLine Type="DotNetOpenAuth.Messages.SignedMessageBase" FixedFromPoint="true">
+ <Path>
+ <Point X="4" Y="1.855" />
+ <Point X="4.25" Y="1.855" />
+ </Path>
+ </InheritanceLine>
+ <TypeIdentifier>
+ <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAA=</HashCode>
+ <FileName>Messages\UnauthorizedTokenRequest.cs</FileName>
+ </TypeIdentifier>
+ </Class>
+ <Class Name="DotNetOpenAuth.Messages.SignedMessageBase">
+ <Position X="0.5" Y="1.5" Width="3.5" />
+ <Members>
+ <Property Name="ITamperResistantOAuthMessage.ConsumerSecret" Hidden="true" />
+ <Property Name="ITamperResistantOAuthMessage.HttpMethod" Hidden="true" />
+ <Property Name="ITamperResistantOAuthMessage.SignatureMethod" Hidden="true" />
+ <Property Name="ITamperResistantOAuthMessage.TokenSecret" Hidden="true" />
+ <Property Name="ITamperResistantProtocolMessage.Signature" Hidden="true" />
+ </Members>
+ <InheritanceLine Type="DotNetOpenAuth.Messages.MessageBase" FixedFromPoint="true" FixedToPoint="true">
+ <Path>
+ <Point X="12.875" Y="1" />
+ <Point X="12.875" Y="0.625" />
+ <Point X="3" Y="0.625" />
+ <Point X="3" Y="1.5" />
+ </Path>
+ </InheritanceLine>
+ <TypeIdentifier>
+ <HashCode>IIAAFAAAAIAAAAAAgICAAgAAAgAAIAQAAAEAIAAQAAE=</HashCode>
+ <FileName>Messages\SignedMessageBase.cs</FileName>
+ </TypeIdentifier>
+ <Lollipop Position="0.2" />
+ </Class>
+ <Interface Name="DotNetOpenAuth.ChannelElements.ITamperResistantOAuthMessage">
+ <Position X="11.25" Y="5.25" Width="2.5" />
+ <TypeIdentifier>
+ <HashCode>AIAAAAAAAAAAAAAAAIAAAgAAAAAAIAQAAAAAAAAAAAA=</HashCode>
+ <FileName>ChannelElements\ITamperResistantOAuthMessage.cs</FileName>
+ </TypeIdentifier>
+ </Interface>
+ <Interface Name="DotNetOpenAuth.Messages.ITokenSecretContainingMessage">
+ <Position X="1" Y="7.5" Width="2" />
+ <TypeIdentifier>
+ <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAA=</HashCode>
+ <FileName>Messages\ITokenSecretContainingMessage.cs</FileName>
+ <NewMemberFileName>Messages\ITokenContainingMessage.cs</NewMemberFileName>
+ </TypeIdentifier>
+ </Interface>
+ <Interface Name="DotNetOpenAuth.Messages.ITokenContainingMessage">
+ <Position X="1" Y="6" Width="2" />
+ <TypeIdentifier>
+ <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAA=</HashCode>
+ <FileName>Messages\ITokenContainingMessage.cs</FileName>
+ </TypeIdentifier>
+ </Interface>
+ <Font Name="Segoe UI" Size="9" />
</ClassDiagram> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs b/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs
index 5d51e42..ee5d46f 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs
+++ b/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs
@@ -1,169 +1,169 @@
-//-----------------------------------------------------------------------
-// <copyright file="SignedMessageBase.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OAuth.Messages {
- using System;
- using System.Diagnostics.CodeAnalysis;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Bindings;
- using DotNetOpenAuth.OAuth.ChannelElements;
-
- /// <summary>
- /// A base class for all signed OAuth messages.
- /// </summary>
- public class SignedMessageBase : MessageBase, ITamperResistantOAuthMessage, IExpiringProtocolMessage, IReplayProtectedProtocolMessage {
- /// <summary>
- /// The reference date and time for calculating time stamps.
- /// </summary>
- private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
-
- /// <summary>
- /// The number of seconds since 1/1/1970, consistent with the OAuth timestamp requirement.
- /// </summary>
- [MessagePart("oauth_timestamp", IsRequired = true)]
- private long timestamp;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="SignedMessageBase"/> class.
- /// </summary>
- /// <param name="transport">A value indicating whether this message requires a direct or indirect transport.</param>
- /// <param name="recipient">The URI that a directed message will be delivered to.</param>
- internal SignedMessageBase(MessageTransport transport, MessageReceivingEndpoint recipient)
- : base(MessageProtections.All, transport, recipient) {
- ITamperResistantOAuthMessage self = (ITamperResistantOAuthMessage)this;
- HttpDeliveryMethods methods = ((IDirectedProtocolMessage)this).HttpMethods;
- self.HttpMethod = (methods & HttpDeliveryMethods.PostRequest) != 0 ? "POST" : "GET";
- }
-
- #region ITamperResistantOAuthMessage Members
-
- /// <summary>
- /// Gets or sets the signature method used to sign the request.
- /// </summary>
- string ITamperResistantOAuthMessage.SignatureMethod {
- get { return this.SignatureMethod; }
- set { this.SignatureMethod = value; }
- }
-
- /// <summary>
- /// Gets or sets the Token Secret used to sign the message.
- /// </summary>
- string ITamperResistantOAuthMessage.TokenSecret {
- get { return this.TokenSecret; }
- set { this.TokenSecret = value; }
- }
-
- /// <summary>
- /// Gets or sets the Consumer key.
- /// </summary>
- [MessagePart("oauth_consumer_key", IsRequired = true)]
- public string ConsumerKey { get; set; }
-
- /// <summary>
- /// Gets or sets the Consumer Secret used to sign the message.
- /// </summary>
- string ITamperResistantOAuthMessage.ConsumerSecret {
- get { return this.ConsumerSecret; }
- set { this.ConsumerSecret = value; }
- }
-
- /// <summary>
- /// Gets or sets the HTTP method that will be used to transmit the message.
- /// </summary>
- string ITamperResistantOAuthMessage.HttpMethod {
- get { return this.HttpMethod; }
- set { this.HttpMethod = value; }
- }
-
- /// <summary>
- /// Gets or sets the URI to the Service Provider endpoint to send this message to.
- /// </summary>
- Uri ITamperResistantOAuthMessage.Recipient {
- get { return this.Recipient; }
- set { this.Recipient = value; }
- }
-
- #endregion
-
- #region ITamperResistantProtocolMessage Members
-
- /// <summary>
- /// Gets or sets the message signature.
- /// </summary>
- string ITamperResistantProtocolMessage.Signature {
- get { return this.Signature; }
- set { this.Signature = value; }
- }
-
- #endregion
-
- #region IExpiringProtocolMessage Members
-
- /// <summary>
- /// Gets or sets the OAuth timestamp of the message.
- /// </summary>
- DateTime IExpiringProtocolMessage.UtcCreationDate {
- get { return epoch + TimeSpan.FromSeconds(this.timestamp); }
- set { this.timestamp = (long)(value - epoch).TotalSeconds; }
- }
-
- #endregion
-
- #region IReplayProtectedProtocolMessage Members
-
- /// <summary>
- /// Gets or sets the message nonce used for replay detection.
- /// </summary>
- [MessagePart("oauth_nonce", IsRequired = true)]
- string IReplayProtectedProtocolMessage.Nonce { get; set; }
-
- #endregion
-
- /// <summary>
- /// Gets or sets the signature method used to sign the request.
- /// </summary>
- [MessagePart("oauth_signature_method", IsRequired = true)]
- protected string SignatureMethod { get; set; }
-
- /// <summary>
- /// Gets or sets the Token Secret used to sign the message.
- /// </summary>
- protected string TokenSecret { get; set; }
-
- /// <summary>
- /// Gets or sets the Consumer Secret used to sign the message.
- /// </summary>
- protected string ConsumerSecret { get; set; }
-
- /// <summary>
- /// Gets or sets the HTTP method that will be used to transmit the message.
- /// </summary>
- protected string HttpMethod { get; set; }
-
- /// <summary>
- /// Gets or sets the message signature.
- /// </summary>
- [MessagePart("oauth_signature", IsRequired = true)]
- protected string Signature { get; set; }
-
- /// <summary>
- /// Gets or sets the version of the protocol this message was created with.
- /// </summary>
- [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Accessed via reflection.")]
- [MessagePart("oauth_version", IsRequired = false)]
- private string Version {
- get {
- return ProtocolVersion.ToString();
- }
-
- set {
- if (value != this.Version) {
- throw new ArgumentOutOfRangeException("value");
- }
- }
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="SignedMessageBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.Messages {
+ using System;
+ using System.Diagnostics.CodeAnalysis;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.OAuth.ChannelElements;
+
+ /// <summary>
+ /// A base class for all signed OAuth messages.
+ /// </summary>
+ public class SignedMessageBase : MessageBase, ITamperResistantOAuthMessage, IExpiringProtocolMessage, IReplayProtectedProtocolMessage {
+ /// <summary>
+ /// The reference date and time for calculating time stamps.
+ /// </summary>
+ private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+
+ /// <summary>
+ /// The number of seconds since 1/1/1970, consistent with the OAuth timestamp requirement.
+ /// </summary>
+ [MessagePart("oauth_timestamp", IsRequired = true)]
+ private long timestamp;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SignedMessageBase"/> class.
+ /// </summary>
+ /// <param name="transport">A value indicating whether this message requires a direct or indirect transport.</param>
+ /// <param name="recipient">The URI that a directed message will be delivered to.</param>
+ internal SignedMessageBase(MessageTransport transport, MessageReceivingEndpoint recipient)
+ : base(MessageProtections.All, transport, recipient) {
+ ITamperResistantOAuthMessage self = (ITamperResistantOAuthMessage)this;
+ HttpDeliveryMethods methods = ((IDirectedProtocolMessage)this).HttpMethods;
+ self.HttpMethod = (methods & HttpDeliveryMethods.PostRequest) != 0 ? "POST" : "GET";
+ }
+
+ #region ITamperResistantOAuthMessage Members
+
+ /// <summary>
+ /// Gets or sets the signature method used to sign the request.
+ /// </summary>
+ string ITamperResistantOAuthMessage.SignatureMethod {
+ get { return this.SignatureMethod; }
+ set { this.SignatureMethod = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the Token Secret used to sign the message.
+ /// </summary>
+ string ITamperResistantOAuthMessage.TokenSecret {
+ get { return this.TokenSecret; }
+ set { this.TokenSecret = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the Consumer key.
+ /// </summary>
+ [MessagePart("oauth_consumer_key", IsRequired = true)]
+ public string ConsumerKey { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Consumer Secret used to sign the message.
+ /// </summary>
+ string ITamperResistantOAuthMessage.ConsumerSecret {
+ get { return this.ConsumerSecret; }
+ set { this.ConsumerSecret = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the HTTP method that will be used to transmit the message.
+ /// </summary>
+ string ITamperResistantOAuthMessage.HttpMethod {
+ get { return this.HttpMethod; }
+ set { this.HttpMethod = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the URI to the Service Provider endpoint to send this message to.
+ /// </summary>
+ Uri ITamperResistantOAuthMessage.Recipient {
+ get { return this.Recipient; }
+ set { this.Recipient = value; }
+ }
+
+ #endregion
+
+ #region ITamperResistantProtocolMessage Members
+
+ /// <summary>
+ /// Gets or sets the message signature.
+ /// </summary>
+ string ITamperResistantProtocolMessage.Signature {
+ get { return this.Signature; }
+ set { this.Signature = value; }
+ }
+
+ #endregion
+
+ #region IExpiringProtocolMessage Members
+
+ /// <summary>
+ /// Gets or sets the OAuth timestamp of the message.
+ /// </summary>
+ DateTime IExpiringProtocolMessage.UtcCreationDate {
+ get { return epoch + TimeSpan.FromSeconds(this.timestamp); }
+ set { this.timestamp = (long)(value - epoch).TotalSeconds; }
+ }
+
+ #endregion
+
+ #region IReplayProtectedProtocolMessage Members
+
+ /// <summary>
+ /// Gets or sets the message nonce used for replay detection.
+ /// </summary>
+ [MessagePart("oauth_nonce", IsRequired = true)]
+ string IReplayProtectedProtocolMessage.Nonce { get; set; }
+
+ #endregion
+
+ /// <summary>
+ /// Gets or sets the signature method used to sign the request.
+ /// </summary>
+ [MessagePart("oauth_signature_method", IsRequired = true)]
+ protected string SignatureMethod { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Token Secret used to sign the message.
+ /// </summary>
+ protected string TokenSecret { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Consumer Secret used to sign the message.
+ /// </summary>
+ protected string ConsumerSecret { get; set; }
+
+ /// <summary>
+ /// Gets or sets the HTTP method that will be used to transmit the message.
+ /// </summary>
+ protected string HttpMethod { get; set; }
+
+ /// <summary>
+ /// Gets or sets the message signature.
+ /// </summary>
+ [MessagePart("oauth_signature", IsRequired = true)]
+ protected string Signature { get; set; }
+
+ /// <summary>
+ /// Gets or sets the version of the protocol this message was created with.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Accessed via reflection.")]
+ [MessagePart("oauth_version", IsRequired = false)]
+ private string OAuthVersion {
+ get {
+ return Version.ToString();
+ }
+
+ set {
+ if (value != this.OAuthVersion) {
+ throw new ArgumentOutOfRangeException("value");
+ }
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/AliasManager.cs b/src/DotNetOpenAuth/OpenId/AliasManager.cs
new file mode 100644
index 0000000..f4bbb04
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/AliasManager.cs
@@ -0,0 +1,138 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Diagnostics;
+using System.Globalization;
+
+namespace DotNetOpenId.Extensions {
+ class AliasManager {
+ readonly string aliasFormat = "alias{0}";
+ /// <summary>
+ /// Tracks extension Type URIs and aliases assigned to them.
+ /// </summary>
+ Dictionary<string, string> typeUriToAliasMap = new Dictionary<string, string>();
+ /// <summary>
+ /// Tracks extension aliases and Type URIs assigned to them.
+ /// </summary>
+ Dictionary<string, string> aliasToTypeUriMap = new Dictionary<string, string>();
+
+ /// <summary>
+ /// Gets an alias assigned for a given Type URI. A new alias is assigned if necessary.
+ /// </summary>
+ public string GetAlias(string typeUri) {
+ if (string.IsNullOrEmpty(typeUri)) throw new ArgumentNullException("typeUri");
+ string alias;
+ if (typeUriToAliasMap.TryGetValue(typeUri, out alias))
+ return alias;
+ else
+ return assignNewAlias(typeUri);
+ }
+
+ /// <summary>
+ /// Sets an alias and the value that will be returned by <see cref="ResolveAlias"/>.
+ /// </summary>
+ public void SetAlias(string alias, string typeUri) {
+ if (string.IsNullOrEmpty(alias)) throw new ArgumentNullException("alias");
+ if (string.IsNullOrEmpty(typeUri)) throw new ArgumentNullException("typeUri");
+ aliasToTypeUriMap.Add(alias, typeUri);
+ typeUriToAliasMap.Add(typeUri, alias);
+ }
+ /// <summary>
+ /// Takes a sequence of type URIs and assigns aliases for all of them.
+ /// </summary>
+ /// <param name="typeUris">The type URIs to create aliases for.</param>
+ /// <param name="preferredTypeUriToAliases">An optional dictionary of URI/alias pairs that suggest preferred aliases to use if available for certain type URIs.</param>
+ public void AssignAliases(IEnumerable<string> typeUris, IDictionary<string, string> preferredTypeUriToAliases) {
+ // First go through the actually used type URIs and see which ones have matching preferred aliases.
+ if (preferredTypeUriToAliases != null) {
+ foreach (string typeUri in typeUris) {
+ if (typeUriToAliasMap.ContainsKey(typeUri)) {
+ // this Type URI is already mapped to an alias.
+ continue;
+ }
+
+ string preferredAlias;
+ if (preferredTypeUriToAliases.TryGetValue(typeUri, out preferredAlias) && !IsAliasUsed(preferredAlias)) {
+ SetAlias(preferredAlias, typeUri);
+ }
+ }
+ }
+
+ // Now go through the whole list again and assign whatever is left now that the preferred ones
+ // have gotten their picks where available.
+ foreach (string typeUri in typeUris) {
+ if (typeUriToAliasMap.ContainsKey(typeUri)) {
+ // this Type URI is already mapped to an alias.
+ continue;
+ }
+
+ assignNewAlias(typeUri);
+ }
+ }
+ /// <summary>
+ /// Sets up aliases for any Type URIs in a dictionary that do not yet have aliases defined,
+ /// and where the given preferred alias is still available.
+ /// </summary>
+ /// <param name="preferredTypeUriToAliases">A dictionary of type URI keys and alias values.</param>
+ public void SetPreferredAliasesWhereNotSet(IDictionary<string, string> preferredTypeUriToAliases) {
+ if (preferredTypeUriToAliases == null) throw new ArgumentNullException("preferredTypeUriToAliases");
+
+ foreach (var pair in preferredTypeUriToAliases) {
+ if (typeUriToAliasMap.ContainsKey(pair.Key)) {
+ // type URI is already mapped
+ continue;
+ }
+
+ if (aliasToTypeUriMap.ContainsKey(pair.Value)) {
+ // alias is already mapped
+ continue;
+ }
+
+ // The type URI and alias are as yet unset, so go ahead and assign them.
+ SetAlias(pair.Value, pair.Key);
+ }
+ }
+
+ /// <summary>
+ /// Gets the Type Uri encoded by a given alias.
+ /// </summary>
+ public string ResolveAlias(string alias) {
+ string typeUri = TryResolveAlias(alias);
+ if (typeUri == null)
+ throw new ArgumentOutOfRangeException("alias");
+ return typeUri;
+ }
+ public string TryResolveAlias(string alias) {
+ if (string.IsNullOrEmpty(alias)) throw new ArgumentNullException("alias");
+ string typeUri = null;
+ aliasToTypeUriMap.TryGetValue(alias, out typeUri);
+ return typeUri;
+ }
+
+ public IEnumerable<string> Aliases {
+ get { return aliasToTypeUriMap.Keys; }
+ }
+ /// <summary>
+ /// Returns a value indicating whether an alias has already been assigned to a type URI.
+ /// </summary>
+ /// <param name="alias">The alias in question.</param>
+ /// <returns>True if the alias has already been assigned. False otherwise.</returns>
+ public bool IsAliasUsed(string alias) {
+ if (string.IsNullOrEmpty(alias)) throw new ArgumentNullException("alias");
+ return aliasToTypeUriMap.ContainsKey(alias);
+ }
+ public bool IsAliasAssignedTo(string typeUri) {
+ if (string.IsNullOrEmpty("typeUri")) throw new ArgumentNullException("typeUri");
+ return typeUriToAliasMap.ContainsKey(typeUri);
+ }
+
+ string assignNewAlias(string typeUri) {
+ Debug.Assert(!string.IsNullOrEmpty(typeUri));
+ Debug.Assert(!typeUriToAliasMap.ContainsKey(typeUri));
+ string alias = string.Format(CultureInfo.InvariantCulture, aliasFormat, typeUriToAliasMap.Count + 1);
+ typeUriToAliasMap.Add(typeUri, alias);
+ aliasToTypeUriMap.Add(alias, typeUri);
+ return alias;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs
new file mode 100644
index 0000000..e28f2aa
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs
@@ -0,0 +1,112 @@
+//-----------------------------------------------------------------------
+// <copyright file="ExtensionsBindingElement.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.Reflection;
+ using DotNetOpenAuth.OpenId.Extensions;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ internal class ExtensionsBindingElement : IChannelBindingElement {
+ #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) {
+ var extendableMessage = message as IProtocolMessageWithExtensions;
+ if (extendableMessage != null) {
+ Protocol protocol = Protocol.Lookup(message.Version);
+ MessageDictionary baseMessageDictionary = new MessageDictionary(message);
+
+ // We have a helper class that will do all the heavy-lifting of organizing
+ // all the extensions, their aliases, and their parameters.
+ var extensionManager = ExtensionArgumentsManager.CreateOutgoingExtensions(protocol);
+ foreach (IExtensionMessage protocolExtension in extendableMessage.Extensions) {
+ var extension = protocolExtension as IOpenIdProtocolMessageExtension;
+ if (extension != null) {
+ var extensionDictionary = new MessageDictionary(extension);
+ extensionManager.AddExtensionArguments(extension.TypeUri, extensionDictionary);
+ } else {
+ Logger.WarnFormat("Unexpected extension type {0} did not implement {1}.", protocolExtension.GetType(), typeof(IOpenIdProtocolMessageExtension).Name);
+ }
+ }
+
+ // We use a cheap trick (for now at least) to determine whether the 'openid.' prefix
+ // belongs on the parameters by just looking at what other parameters do.
+ // Technically, direct message responses from Provider to Relying Party are the only
+ // messages that leave off the 'openid.' prefix.
+ bool includeOpenIdPrefix = baseMessageDictionary.Keys.Any(key => key.StartsWith(protocol.openid.Prefix, StringComparison.Ordinal));
+
+ // Add the extension parameters to the base message for transmission.
+ extendableMessage.AddExtraParameters(extensionManager.GetArgumentsToSend(includeOpenIdPrefix));
+ 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) {
+ var extendableMessage = message as IProtocolMessageWithExtensions;
+ if (extendableMessage != null) {
+ // TODO: Implement this
+ throw new NotImplementedException();
+ ////return true;
+ }
+
+ return false;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs
index 64b3b60..b691a0b 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/SigningBindingElement.cs
@@ -1,274 +1,274 @@
-//-----------------------------------------------------------------------
-// <copyright file="SigningBindingElement.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.ChannelElements {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Linq;
- using System.Net.Security;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Bindings;
- using DotNetOpenAuth.Messaging.Reflection;
- using DotNetOpenAuth.OpenId.Messages;
-
- /// <summary>
- /// Signs and verifies authentication assertions.
- /// </summary>
- internal class SigningBindingElement : IChannelBindingElement {
- /// <summary>
- /// The association store used by Relying Parties to look up the secrets needed for signing.
- /// </summary>
- private readonly IAssociationStore<Uri> rpAssociations;
-
- /// <summary>
- /// The association store used by Providers to look up the secrets needed for signing.
- /// </summary>
- private readonly IAssociationStore<AssociationRelyingPartyType> opAssociations;
-
- /// <summary>
- /// Initializes a new instance of the SigningBindingElement class for use by a Relying Party.
- /// </summary>
- /// <param name="associations">The association store used to look up the secrets needed for signing.</param>
- internal SigningBindingElement(IAssociationStore<Uri> associations) {
- ErrorUtilities.VerifyArgumentNotNull(associations, "associations");
-
- this.rpAssociations = associations;
- }
-
- /// <summary>
- /// Initializes a new instance of the SigningBindingElement class for use by a Provider.
- /// </summary>
- /// <param name="associations">The association store used to look up the secrets needed for signing.</param>
- internal SigningBindingElement(IAssociationStore<AssociationRelyingPartyType> associations) {
- ErrorUtilities.VerifyArgumentNotNull(associations, "associations");
-
- this.opAssociations = associations;
- }
-
- #region IChannelBindingElement Properties
-
- /// <summary>
- /// Gets the protection offered (if any) by this binding element.
- /// </summary>
- /// <value><see cref="MessageProtections.TamperProtection"/></value>
- public MessageProtections Protection {
- get { return MessageProtections.TamperProtection; }
- }
-
- /// <summary>
- /// Gets or sets the channel that this binding element belongs to.
- /// </summary>
- public Channel Channel { get; set; }
-
- /// <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>
- public bool PrepareMessageForSending(IProtocolMessage message) {
- var signedMessage = message as ITamperResistantOpenIdMessage;
- if (signedMessage != null) {
- Logger.DebugFormat("Signing {0} message.", message.GetType().Name);
- Association association = this.GetAssociation(signedMessage);
- signedMessage.AssociationHandle = association.Handle;
- signedMessage.SignedParameterOrder = GetSignedParameterOrder(signedMessage);
- signedMessage.Signature = this.GetSignature(signedMessage, association);
- 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>
- public bool PrepareMessageForReceiving(IProtocolMessage message) {
- var signedMessage = message as ITamperResistantOpenIdMessage;
- if (signedMessage != null) {
- Logger.DebugFormat("Verifying incoming {0} message signature of: {1}", message.GetType().Name, signedMessage.Signature);
-
- EnsureParametersRequiringSignatureAreSigned(signedMessage);
-
- Association association = this.GetSpecificAssociation(signedMessage);
- if (association != null) {
- string signature = this.GetSignature(signedMessage, association);
- if (!string.Equals(signedMessage.Signature, signature, StringComparison.Ordinal)) {
- Logger.Error("Signature verification failed.");
- throw new InvalidSignatureException(message);
- }
- } else {
- ErrorUtilities.VerifyInternal(this.Channel != null, "Cannot verify private association signature because we don't have a channel.");
-
- // We did not recognize the association the provider used to sign the message.
- // Ask the provider to check the signature then.
- var checkSignatureRequest = new CheckAuthenticationRequest((IndirectSignedResponse)signedMessage);
- var checkSignatureResponse = this.Channel.Request<CheckAuthenticationResponse>(checkSignatureRequest);
- if (!checkSignatureResponse.IsValid) {
- Logger.Error("Provider reports signature verification failed.");
- throw new InvalidSignatureException(message);
- }
- }
-
- return true;
- }
-
- return false;
- }
-
- #endregion
-
- /// <summary>
- /// Ensures that all message parameters that must be signed are in fact included
- /// in the signature.
- /// </summary>
- /// <param name="signedMessage">The signed message.</param>
- private static void EnsureParametersRequiringSignatureAreSigned(ITamperResistantOpenIdMessage signedMessage) {
- // Verify that the signed parameter order includes the mandated fields.
- // We do this in such a way that derived classes that add mandated fields automatically
- // get included in the list of checked parameters.
- Protocol protocol = Protocol.Lookup(signedMessage.ProtocolVersion);
- var partsRequiringProtection = from part in MessageDescription.Get(signedMessage.GetType(), signedMessage.ProtocolVersion).Mapping.Values
- where part.RequiredProtection != ProtectionLevel.None
- select part.Name;
- ErrorUtilities.VerifyInternal(partsRequiringProtection.All(name => name.StartsWith(protocol.openid.Prefix, StringComparison.Ordinal)), "Signing only works when the parameters start with the 'openid.' prefix.");
- string[] signedParts = signedMessage.SignedParameterOrder.Split(',');
- var unsignedParts = from partName in partsRequiringProtection
- where !signedParts.Contains(partName.Substring(protocol.openid.Prefix.Length))
- select partName;
- ErrorUtilities.VerifyProtocol(!unsignedParts.Any(), OpenIdStrings.SignatureDoesNotIncludeMandatoryParts, string.Join(", ", unsignedParts.ToArray()));
- }
-
- /// <summary>
- /// Gets the value to use for the openid.signed parameter.
- /// </summary>
- /// <param name="signedMessage">The signable message.</param>
- /// <returns>
- /// A comma-delimited list of parameter names, omitting the 'openid.' prefix, that determines
- /// the inclusion and order of message parts that will be signed.
- /// </returns>
- private static string GetSignedParameterOrder(ITamperResistantOpenIdMessage signedMessage) {
- ErrorUtilities.VerifyArgumentNotNull(signedMessage, "signedMessage");
-
- MessageDescription description = MessageDescription.Get(signedMessage.GetType(), signedMessage.ProtocolVersion);
- var signedParts = from part in description.Mapping.Values
- where (part.RequiredProtection & System.Net.Security.ProtectionLevel.Sign) != 0
- && part.GetValue(signedMessage) != null
- select part.Name;
- string prefix = Protocol.V20.openid.Prefix;
- Debug.Assert(signedParts.All(name => name.StartsWith(prefix, StringComparison.Ordinal)), "All signed message parts must start with 'openid.'.");
- int skipLength = prefix.Length;
- string signedFields = string.Join(",", signedParts.Select(name => name.Substring(skipLength)).ToArray());
- return signedFields;
- }
-
- /// <summary>
- /// Calculates the signature for a given message.
- /// </summary>
- /// <param name="signedMessage">The message to sign or verify.</param>
- /// <param name="association">The association to use to sign the message.</param>
- /// <returns>The calculated signature of the method.</returns>
- private string GetSignature(ITamperResistantOpenIdMessage signedMessage, Association association) {
- ErrorUtilities.VerifyArgumentNotNull(signedMessage, "signedMessage");
- ErrorUtilities.VerifyNonZeroLength(signedMessage.SignedParameterOrder, "signedMessage.SignedParameterOrder");
- ErrorUtilities.VerifyArgumentNotNull(association, "association");
-
- // Prepare the parts to sign, taking care to replace an openid.mode value
- // of check_authentication with its original id_res so the signature matches.
- Protocol protocol = Protocol.Lookup(signedMessage.ProtocolVersion);
- MessageDictionary dictionary = new MessageDictionary(signedMessage);
- var parametersToSign = from name in signedMessage.SignedParameterOrder.Split(',')
- let prefixedName = Protocol.V20.openid.Prefix + name
- select new KeyValuePair<string, string>(prefixedName, dictionary[prefixedName]);
-
- byte[] dataToSign = KeyValueFormEncoding.GetBytes(parametersToSign);
- return Convert.ToBase64String(association.Sign(dataToSign));
- }
-
- /// <summary>
- /// Gets the association to use to sign or verify a message.
- /// </summary>
- /// <param name="signedMessage">The message to sign or verify.</param>
- /// <returns>The association to use to sign or verify the message.</returns>
- private Association GetAssociation(ITamperResistantOpenIdMessage signedMessage) {
- if (this.rpAssociations != null) {
- // We're on a Relying Party verifying a signature.
- IDirectedProtocolMessage directedMessage = (IDirectedProtocolMessage)signedMessage;
- return this.rpAssociations.GetAssociation(directedMessage.Recipient, signedMessage.AssociationHandle);
- } else {
- // We're on a Provider to either sign (smart/dumb) or verify a dumb signature.
- return this.GetSpecificAssociation(signedMessage) ?? this.GetDumbAssociationForSigning();
- }
- }
-
- /// <summary>
- /// Gets a specific association referenced in a given message's association handle.
- /// </summary>
- /// <param name="signedMessage">The signed message whose association handle should be used to lookup the association to return.</param>
- /// <returns>The referenced association; or <c>null</c> if such an association cannot be found.</returns>
- /// <remarks>
- /// If the association handle set in the message does not match any valid association,
- /// the association handle property is cleared, and the
- /// <see cref="ITamperResistantOpenIdMessage.InvalidateHandle"/> property is set to the
- /// handle that could not be found.
- /// </remarks>
- private Association GetSpecificAssociation(ITamperResistantOpenIdMessage signedMessage) {
- Association association = null;
-
- if (!string.IsNullOrEmpty(signedMessage.AssociationHandle)) {
- if (this.opAssociations != null) {
- // Since we have an association handle, we're either signing with a smart association,
- // or verifying a dumb one.
- bool signing = string.IsNullOrEmpty(signedMessage.Signature);
- ErrorUtilities.VerifyInternal(signing == (signedMessage is PositiveAssertionResponse), "Ooops... somehow we think we're signing a message that isn't a positive assertion!");
- AssociationRelyingPartyType type = signing ? AssociationRelyingPartyType.Smart : AssociationRelyingPartyType.Dumb;
- association = this.opAssociations.GetAssociation(type, signedMessage.AssociationHandle);
- if (association == null) {
- // There was no valid association with the requested handle.
- // Let's tell the RP to forget about that association.
- signedMessage.InvalidateHandle = signedMessage.AssociationHandle;
- signedMessage.AssociationHandle = null;
- }
- } else {
- Uri providerEndpoint = ((PositiveAssertionResponse)signedMessage).ProviderEndpoint;
- association = this.rpAssociations.GetAssociation(providerEndpoint, signedMessage.AssociationHandle);
- }
- }
-
- return association;
- }
-
- /// <summary>
- /// Gets a private Provider association used for signing messages in "dumb" mode.
- /// </summary>
- /// <returns>An existing or newly created association.</returns>
- private Association GetDumbAssociationForSigning() {
- // If no assoc_handle was given or it was invalid, the only thing
- // left to do is sign a message using a 'dumb' mode association.
- Protocol protocol = Protocol.Default;
- Association association = this.opAssociations.GetAssociation(AssociationRelyingPartyType.Dumb);
- if (association == null) {
- association = HmacShaAssociation.Create(protocol, protocol.Args.SignatureAlgorithm.HMAC_SHA256, AssociationRelyingPartyType.Dumb);
- this.opAssociations.StoreAssociation(AssociationRelyingPartyType.Dumb, association);
- }
-
- return association;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="SigningBindingElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Linq;
+ using System.Net.Security;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.Messaging.Reflection;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// Signs and verifies authentication assertions.
+ /// </summary>
+ internal class SigningBindingElement : IChannelBindingElement {
+ /// <summary>
+ /// The association store used by Relying Parties to look up the secrets needed for signing.
+ /// </summary>
+ private readonly IAssociationStore<Uri> rpAssociations;
+
+ /// <summary>
+ /// The association store used by Providers to look up the secrets needed for signing.
+ /// </summary>
+ private readonly IAssociationStore<AssociationRelyingPartyType> opAssociations;
+
+ /// <summary>
+ /// Initializes a new instance of the SigningBindingElement class for use by a Relying Party.
+ /// </summary>
+ /// <param name="associations">The association store used to look up the secrets needed for signing.</param>
+ internal SigningBindingElement(IAssociationStore<Uri> associations) {
+ ErrorUtilities.VerifyArgumentNotNull(associations, "associations");
+
+ this.rpAssociations = associations;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the SigningBindingElement class for use by a Provider.
+ /// </summary>
+ /// <param name="associations">The association store used to look up the secrets needed for signing.</param>
+ internal SigningBindingElement(IAssociationStore<AssociationRelyingPartyType> associations) {
+ ErrorUtilities.VerifyArgumentNotNull(associations, "associations");
+
+ this.opAssociations = associations;
+ }
+
+ #region IChannelBindingElement Properties
+
+ /// <summary>
+ /// Gets the protection offered (if any) by this binding element.
+ /// </summary>
+ /// <value><see cref="MessageProtections.TamperProtection"/></value>
+ public MessageProtections Protection {
+ get { return MessageProtections.TamperProtection; }
+ }
+
+ /// <summary>
+ /// Gets or sets the channel that this binding element belongs to.
+ /// </summary>
+ public Channel Channel { get; set; }
+
+ /// <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>
+ public bool PrepareMessageForSending(IProtocolMessage message) {
+ var signedMessage = message as ITamperResistantOpenIdMessage;
+ if (signedMessage != null) {
+ Logger.DebugFormat("Signing {0} message.", message.GetType().Name);
+ Association association = this.GetAssociation(signedMessage);
+ signedMessage.AssociationHandle = association.Handle;
+ signedMessage.SignedParameterOrder = GetSignedParameterOrder(signedMessage);
+ signedMessage.Signature = this.GetSignature(signedMessage, association);
+ 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>
+ public bool PrepareMessageForReceiving(IProtocolMessage message) {
+ var signedMessage = message as ITamperResistantOpenIdMessage;
+ if (signedMessage != null) {
+ Logger.DebugFormat("Verifying incoming {0} message signature of: {1}", message.GetType().Name, signedMessage.Signature);
+
+ EnsureParametersRequiringSignatureAreSigned(signedMessage);
+
+ Association association = this.GetSpecificAssociation(signedMessage);
+ if (association != null) {
+ string signature = this.GetSignature(signedMessage, association);
+ if (!string.Equals(signedMessage.Signature, signature, StringComparison.Ordinal)) {
+ Logger.Error("Signature verification failed.");
+ throw new InvalidSignatureException(message);
+ }
+ } else {
+ ErrorUtilities.VerifyInternal(this.Channel != null, "Cannot verify private association signature because we don't have a channel.");
+
+ // We did not recognize the association the provider used to sign the message.
+ // Ask the provider to check the signature then.
+ var checkSignatureRequest = new CheckAuthenticationRequest((IndirectSignedResponse)signedMessage);
+ var checkSignatureResponse = this.Channel.Request<CheckAuthenticationResponse>(checkSignatureRequest);
+ if (!checkSignatureResponse.IsValid) {
+ Logger.Error("Provider reports signature verification failed.");
+ throw new InvalidSignatureException(message);
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Ensures that all message parameters that must be signed are in fact included
+ /// in the signature.
+ /// </summary>
+ /// <param name="signedMessage">The signed message.</param>
+ private static void EnsureParametersRequiringSignatureAreSigned(ITamperResistantOpenIdMessage signedMessage) {
+ // Verify that the signed parameter order includes the mandated fields.
+ // We do this in such a way that derived classes that add mandated fields automatically
+ // get included in the list of checked parameters.
+ Protocol protocol = Protocol.Lookup(signedMessage.Version);
+ var partsRequiringProtection = from part in MessageDescription.Get(signedMessage.GetType(), signedMessage.Version).Mapping.Values
+ where part.RequiredProtection != ProtectionLevel.None
+ select part.Name;
+ ErrorUtilities.VerifyInternal(partsRequiringProtection.All(name => name.StartsWith(protocol.openid.Prefix, StringComparison.Ordinal)), "Signing only works when the parameters start with the 'openid.' prefix.");
+ string[] signedParts = signedMessage.SignedParameterOrder.Split(',');
+ var unsignedParts = from partName in partsRequiringProtection
+ where !signedParts.Contains(partName.Substring(protocol.openid.Prefix.Length))
+ select partName;
+ ErrorUtilities.VerifyProtocol(!unsignedParts.Any(), OpenIdStrings.SignatureDoesNotIncludeMandatoryParts, string.Join(", ", unsignedParts.ToArray()));
+ }
+
+ /// <summary>
+ /// Gets the value to use for the openid.signed parameter.
+ /// </summary>
+ /// <param name="signedMessage">The signable message.</param>
+ /// <returns>
+ /// A comma-delimited list of parameter names, omitting the 'openid.' prefix, that determines
+ /// the inclusion and order of message parts that will be signed.
+ /// </returns>
+ private static string GetSignedParameterOrder(ITamperResistantOpenIdMessage signedMessage) {
+ ErrorUtilities.VerifyArgumentNotNull(signedMessage, "signedMessage");
+
+ MessageDescription description = MessageDescription.Get(signedMessage.GetType(), signedMessage.Version);
+ var signedParts = from part in description.Mapping.Values
+ where (part.RequiredProtection & System.Net.Security.ProtectionLevel.Sign) != 0
+ && part.GetValue(signedMessage) != null
+ select part.Name;
+ string prefix = Protocol.V20.openid.Prefix;
+ Debug.Assert(signedParts.All(name => name.StartsWith(prefix, StringComparison.Ordinal)), "All signed message parts must start with 'openid.'.");
+ int skipLength = prefix.Length;
+ string signedFields = string.Join(",", signedParts.Select(name => name.Substring(skipLength)).ToArray());
+ return signedFields;
+ }
+
+ /// <summary>
+ /// Calculates the signature for a given message.
+ /// </summary>
+ /// <param name="signedMessage">The message to sign or verify.</param>
+ /// <param name="association">The association to use to sign the message.</param>
+ /// <returns>The calculated signature of the method.</returns>
+ private string GetSignature(ITamperResistantOpenIdMessage signedMessage, Association association) {
+ ErrorUtilities.VerifyArgumentNotNull(signedMessage, "signedMessage");
+ ErrorUtilities.VerifyNonZeroLength(signedMessage.SignedParameterOrder, "signedMessage.SignedParameterOrder");
+ ErrorUtilities.VerifyArgumentNotNull(association, "association");
+
+ // Prepare the parts to sign, taking care to replace an openid.mode value
+ // of check_authentication with its original id_res so the signature matches.
+ Protocol protocol = Protocol.Lookup(signedMessage.Version);
+ MessageDictionary dictionary = new MessageDictionary(signedMessage);
+ var parametersToSign = from name in signedMessage.SignedParameterOrder.Split(',')
+ let prefixedName = Protocol.V20.openid.Prefix + name
+ select new KeyValuePair<string, string>(prefixedName, dictionary[prefixedName]);
+
+ byte[] dataToSign = KeyValueFormEncoding.GetBytes(parametersToSign);
+ return Convert.ToBase64String(association.Sign(dataToSign));
+ }
+
+ /// <summary>
+ /// Gets the association to use to sign or verify a message.
+ /// </summary>
+ /// <param name="signedMessage">The message to sign or verify.</param>
+ /// <returns>The association to use to sign or verify the message.</returns>
+ private Association GetAssociation(ITamperResistantOpenIdMessage signedMessage) {
+ if (this.rpAssociations != null) {
+ // We're on a Relying Party verifying a signature.
+ IDirectedProtocolMessage directedMessage = (IDirectedProtocolMessage)signedMessage;
+ return this.rpAssociations.GetAssociation(directedMessage.Recipient, signedMessage.AssociationHandle);
+ } else {
+ // We're on a Provider to either sign (smart/dumb) or verify a dumb signature.
+ return this.GetSpecificAssociation(signedMessage) ?? this.GetDumbAssociationForSigning();
+ }
+ }
+
+ /// <summary>
+ /// Gets a specific association referenced in a given message's association handle.
+ /// </summary>
+ /// <param name="signedMessage">The signed message whose association handle should be used to lookup the association to return.</param>
+ /// <returns>The referenced association; or <c>null</c> if such an association cannot be found.</returns>
+ /// <remarks>
+ /// If the association handle set in the message does not match any valid association,
+ /// the association handle property is cleared, and the
+ /// <see cref="ITamperResistantOpenIdMessage.InvalidateHandle"/> property is set to the
+ /// handle that could not be found.
+ /// </remarks>
+ private Association GetSpecificAssociation(ITamperResistantOpenIdMessage signedMessage) {
+ Association association = null;
+
+ if (!string.IsNullOrEmpty(signedMessage.AssociationHandle)) {
+ if (this.opAssociations != null) {
+ // Since we have an association handle, we're either signing with a smart association,
+ // or verifying a dumb one.
+ bool signing = string.IsNullOrEmpty(signedMessage.Signature);
+ ErrorUtilities.VerifyInternal(signing == (signedMessage is PositiveAssertionResponse), "Ooops... somehow we think we're signing a message that isn't a positive assertion!");
+ AssociationRelyingPartyType type = signing ? AssociationRelyingPartyType.Smart : AssociationRelyingPartyType.Dumb;
+ association = this.opAssociations.GetAssociation(type, signedMessage.AssociationHandle);
+ if (association == null) {
+ // There was no valid association with the requested handle.
+ // Let's tell the RP to forget about that association.
+ signedMessage.InvalidateHandle = signedMessage.AssociationHandle;
+ signedMessage.AssociationHandle = null;
+ }
+ } else {
+ Uri providerEndpoint = ((PositiveAssertionResponse)signedMessage).ProviderEndpoint;
+ association = this.rpAssociations.GetAssociation(providerEndpoint, signedMessage.AssociationHandle);
+ }
+ }
+
+ return association;
+ }
+
+ /// <summary>
+ /// Gets a private Provider association used for signing messages in "dumb" mode.
+ /// </summary>
+ /// <returns>An existing or newly created association.</returns>
+ private Association GetDumbAssociationForSigning() {
+ // If no assoc_handle was given or it was invalid, the only thing
+ // left to do is sign a message using a 'dumb' mode association.
+ Protocol protocol = Protocol.Default;
+ Association association = this.opAssociations.GetAssociation(AssociationRelyingPartyType.Dumb);
+ if (association == null) {
+ association = HmacShaAssociation.Create(protocol, protocol.Args.SignatureAlgorithm.HMAC_SHA256, AssociationRelyingPartyType.Dumb);
+ this.opAssociations.StoreAssociation(AssociationRelyingPartyType.Dumb, association);
+ }
+
+ return association;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/AliasManager.cs b/src/DotNetOpenAuth/OpenId/Extensions/AliasManager.cs
new file mode 100644
index 0000000..ee9f288
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Extensions/AliasManager.cs
@@ -0,0 +1,150 @@
+//-----------------------------------------------------------------------
+// <copyright file="AliasManager.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions {
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+ using System.Diagnostics;
+ using System.Globalization;
+
+ internal class AliasManager {
+ private readonly string aliasFormat = "alias{0}";
+
+ /// <summary>
+ /// Tracks extension Type URIs and aliases assigned to them.
+ /// </summary>
+ private Dictionary<string, string> typeUriToAliasMap = new Dictionary<string, string>();
+
+ /// <summary>
+ /// Tracks extension aliases and Type URIs assigned to them.
+ /// </summary>
+ private Dictionary<string, string> aliasToTypeUriMap = new Dictionary<string, string>();
+
+ /// <summary>
+ /// Gets an alias assigned for a given Type URI. A new alias is assigned if necessary.
+ /// </summary>
+ public string GetAlias(string typeUri) {
+ if (string.IsNullOrEmpty(typeUri)) throw new ArgumentNullException("typeUri");
+ string alias;
+ if (typeUriToAliasMap.TryGetValue(typeUri, out alias))
+ return alias;
+ else
+ return assignNewAlias(typeUri);
+ }
+
+ /// <summary>
+ /// Sets an alias and the value that will be returned by <see cref="ResolveAlias"/>.
+ /// </summary>
+ public void SetAlias(string alias, string typeUri) {
+ if (string.IsNullOrEmpty(alias)) throw new ArgumentNullException("alias");
+ if (string.IsNullOrEmpty(typeUri)) throw new ArgumentNullException("typeUri");
+ aliasToTypeUriMap.Add(alias, typeUri);
+ typeUriToAliasMap.Add(typeUri, alias);
+ }
+ /// <summary>
+ /// Takes a sequence of type URIs and assigns aliases for all of them.
+ /// </summary>
+ /// <param name="typeUris">The type URIs to create aliases for.</param>
+ /// <param name="preferredTypeUriToAliases">An optional dictionary of URI/alias pairs that suggest preferred aliases to use if available for certain type URIs.</param>
+ public void AssignAliases(IEnumerable<string> typeUris, IDictionary<string, string> preferredTypeUriToAliases) {
+ // First go through the actually used type URIs and see which ones have matching preferred aliases.
+ if (preferredTypeUriToAliases != null) {
+ foreach (string typeUri in typeUris) {
+ if (typeUriToAliasMap.ContainsKey(typeUri)) {
+ // this Type URI is already mapped to an alias.
+ continue;
+ }
+
+ string preferredAlias;
+ if (preferredTypeUriToAliases.TryGetValue(typeUri, out preferredAlias) && !IsAliasUsed(preferredAlias)) {
+ SetAlias(preferredAlias, typeUri);
+ }
+ }
+ }
+
+ // Now go through the whole list again and assign whatever is left now that the preferred ones
+ // have gotten their picks where available.
+ foreach (string typeUri in typeUris) {
+ if (typeUriToAliasMap.ContainsKey(typeUri)) {
+ // this Type URI is already mapped to an alias.
+ continue;
+ }
+
+ assignNewAlias(typeUri);
+ }
+ }
+
+ /// <summary>
+ /// Sets up aliases for any Type URIs in a dictionary that do not yet have aliases defined,
+ /// and where the given preferred alias is still available.
+ /// </summary>
+ /// <param name="preferredTypeUriToAliases">A dictionary of type URI keys and alias values.</param>
+ public void SetPreferredAliasesWhereNotSet(IDictionary<string, string> preferredTypeUriToAliases) {
+ if (preferredTypeUriToAliases == null) throw new ArgumentNullException("preferredTypeUriToAliases");
+
+ foreach (var pair in preferredTypeUriToAliases) {
+ if (typeUriToAliasMap.ContainsKey(pair.Key)) {
+ // type URI is already mapped
+ continue;
+ }
+
+ if (aliasToTypeUriMap.ContainsKey(pair.Value)) {
+ // alias is already mapped
+ continue;
+ }
+
+ // The type URI and alias are as yet unset, so go ahead and assign them.
+ SetAlias(pair.Value, pair.Key);
+ }
+ }
+
+ /// <summary>
+ /// Gets the Type Uri encoded by a given alias.
+ /// </summary>
+ public string ResolveAlias(string alias) {
+ string typeUri = TryResolveAlias(alias);
+ if (typeUri == null)
+ throw new ArgumentOutOfRangeException("alias");
+ return typeUri;
+ }
+
+ public string TryResolveAlias(string alias) {
+ if (string.IsNullOrEmpty(alias)) throw new ArgumentNullException("alias");
+ string typeUri = null;
+ aliasToTypeUriMap.TryGetValue(alias, out typeUri);
+ return typeUri;
+ }
+
+ public IEnumerable<string> Aliases {
+ get { return aliasToTypeUriMap.Keys; }
+ }
+
+ /// <summary>
+ /// Returns a value indicating whether an alias has already been assigned to a type URI.
+ /// </summary>
+ /// <param name="alias">The alias in question.</param>
+ /// <returns>True if the alias has already been assigned. False otherwise.</returns>
+ public bool IsAliasUsed(string alias) {
+ if (string.IsNullOrEmpty(alias)) throw new ArgumentNullException("alias");
+ return aliasToTypeUriMap.ContainsKey(alias);
+ }
+
+ public bool IsAliasAssignedTo(string typeUri) {
+ if (string.IsNullOrEmpty("typeUri")) throw new ArgumentNullException("typeUri");
+ return typeUriToAliasMap.ContainsKey(typeUri);
+ }
+
+ string assignNewAlias(string typeUri) {
+ Debug.Assert(!string.IsNullOrEmpty(typeUri));
+ Debug.Assert(!typeUriToAliasMap.ContainsKey(typeUri));
+ string alias = string.Format(CultureInfo.InvariantCulture, aliasFormat, typeUriToAliasMap.Count + 1);
+ typeUriToAliasMap.Add(typeUri, alias);
+ aliasToTypeUriMap.Add(alias, typeUri);
+ return alias;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ExtensionArgumentsManager.cs b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionArgumentsManager.cs
new file mode 100644
index 0000000..d41d1e4
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionArgumentsManager.cs
@@ -0,0 +1,149 @@
+//-----------------------------------------------------------------------
+// <copyright file="ExtensionArgumentsManager.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions {
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+ using System.Globalization;
+ using DotNetOpenAuth.Messaging;
+
+ internal class ExtensionArgumentsManager : IIncomingExtensions, IOutgoingExtensions {
+ private Protocol protocol;
+
+ /// <summary>
+ /// Whether extensions are being read or written.
+ /// </summary>
+ private bool isReadMode;
+
+ private Extensions.AliasManager aliasManager = new Extensions.AliasManager();
+
+ /// <summary>
+ /// A complex dictionary where the key is the Type URI of the extension,
+ /// and the value is another dictionary of the name/value args of the extension.
+ /// </summary>
+ private Dictionary<string, IDictionary<string, string>> extensions = new Dictionary<string, IDictionary<string, string>>();
+
+ /// <summary>
+ /// This contains a set of aliases that we must be willing to implicitly
+ /// match to namespaces for backward compatibility with other OpenID libraries.
+ /// </summary>
+ private static readonly Dictionary<string, string> typeUriToAliasAffinity = new Dictionary<string, string> {
+ // TODO: re-enable these lines.
+ ////{ Extensions.SimpleRegistration.Constants.sreg_ns, Extensions.SimpleRegistration.Constants.sreg_compatibility_alias },
+ ////{ Extensions.ProviderAuthenticationPolicy.Constants.TypeUri, Extensions.ProviderAuthenticationPolicy.Constants.pape_compatibility_alias },
+ };
+
+ private ExtensionArgumentsManager() { }
+
+ public static ExtensionArgumentsManager CreateIncomingExtensions(IDictionary<string, string> query) {
+ if (query == null) throw new ArgumentNullException("query");
+ var mgr = new ExtensionArgumentsManager();
+ mgr.protocol = Protocol.Detect(query);
+ mgr.isReadMode = true;
+ string aliasPrefix = mgr.protocol.openid.ns + ".";
+ // First pass looks for namespace aliases
+ foreach (var pair in query) {
+ if (pair.Key.StartsWith(aliasPrefix, StringComparison.Ordinal)) {
+ mgr.aliasManager.SetAlias(pair.Key.Substring(aliasPrefix.Length), pair.Value);
+ }
+ }
+ // For backwards compatibility, add certain aliases if they aren't defined.
+ foreach (var pair in typeUriToAliasAffinity) {
+ if (!mgr.aliasManager.IsAliasAssignedTo(pair.Key) &&
+ !mgr.aliasManager.IsAliasUsed(pair.Value)) {
+ mgr.aliasManager.SetAlias(pair.Value, pair.Key);
+ }
+ }
+ // Second pass looks for extensions using those aliases
+ foreach (var pair in query) {
+ if (!pair.Key.StartsWith(mgr.protocol.openid.Prefix, StringComparison.Ordinal)) continue;
+ string possibleAlias = pair.Key.Substring(mgr.protocol.openid.Prefix.Length);
+ int periodIndex = possibleAlias.IndexOf(".", StringComparison.Ordinal);
+ if (periodIndex >= 0) possibleAlias = possibleAlias.Substring(0, periodIndex);
+ string typeUri;
+ if ((typeUri = mgr.aliasManager.TryResolveAlias(possibleAlias)) != null) {
+ if (!mgr.extensions.ContainsKey(typeUri))
+ mgr.extensions[typeUri] = new Dictionary<string, string>();
+ string key = periodIndex >= 0 ? pair.Key.Substring(mgr.protocol.openid.Prefix.Length + possibleAlias.Length + 1) : string.Empty;
+ mgr.extensions[typeUri].Add(key, pair.Value);
+ }
+ }
+ return mgr;
+ }
+
+ public static ExtensionArgumentsManager CreateOutgoingExtensions(Protocol protocol) {
+ var mgr = new ExtensionArgumentsManager();
+ mgr.protocol = protocol;
+ // Affinity for certain alias for backwards compatibility
+ foreach (var pair in typeUriToAliasAffinity) {
+ mgr.aliasManager.SetAlias(pair.Value, pair.Key);
+ }
+ return mgr;
+ }
+
+ /// <summary>
+ /// Gets the actual arguments to add to a querystring or other response,
+ /// where type URI, alias, and actual key/values are all defined.
+ /// </summary>
+ public IDictionary<string, string> GetArgumentsToSend(bool includeOpenIdPrefix) {
+ if (isReadMode) throw new InvalidOperationException();
+ Dictionary<string, string> args = new Dictionary<string, string>();
+ foreach (var typeUriAndExtension in extensions) {
+ string typeUri = typeUriAndExtension.Key;
+ var extensionArgs = typeUriAndExtension.Value;
+ if (extensionArgs.Count == 0) continue;
+ string alias = aliasManager.GetAlias(typeUri);
+ // send out the alias declaration
+ string openidPrefix = includeOpenIdPrefix ? protocol.openid.Prefix : string.Empty;
+ args.Add(openidPrefix + protocol.openidnp.ns + "." + alias, typeUri);
+ string prefix = openidPrefix + alias;
+ foreach (var pair in extensionArgs) {
+ string key = prefix;
+ if (pair.Key.Length > 0) key += "." + pair.Key;
+ args.Add(key, pair.Value);
+ }
+ }
+ return args;
+ }
+
+ public void AddExtensionArguments(string extensionTypeUri, IDictionary<string, string> arguments) {
+ if (isReadMode) {
+ throw new InvalidOperationException();
+ }
+ ErrorUtilities.VerifyNonZeroLength(extensionTypeUri, "extensionTypeUri");
+ ErrorUtilities.VerifyArgumentNotNull(arguments, "arguments");
+ if (arguments.Count == 0) return;
+
+ IDictionary<string, string> extensionArgs;
+ if (!extensions.TryGetValue(extensionTypeUri, out extensionArgs)) {
+ extensions.Add(extensionTypeUri, extensionArgs = new Dictionary<string, string>());
+ }
+
+ ErrorUtilities.VerifyProtocol(extensionArgs.Count == 0, OpenIdStrings.ExtensionAlreadyAddedWithSameTypeURI, extensionTypeUri);
+ foreach (var pair in arguments) {
+ extensionArgs.Add(pair.Key, pair.Value);
+ }
+ }
+
+ /// <summary>
+ /// Gets the fields carried by a given OpenId extension.
+ /// </summary>
+ /// <returns>The fields included in the given extension, or null if the extension is not present.</returns>
+ public IDictionary<string, string> GetExtensionArguments(string extensionTypeUri) {
+ if (!isReadMode) throw new InvalidOperationException();
+ if (string.IsNullOrEmpty(extensionTypeUri)) throw new ArgumentNullException("extensionTypeUri");
+ IDictionary<string, string> extensionArgs;
+ extensions.TryGetValue(extensionTypeUri, out extensionArgs);
+ return extensionArgs;
+ }
+
+ public bool ContainsExtension(string extensionTypeUri) {
+ if (!isReadMode) throw new InvalidOperationException();
+ return extensions.ContainsKey(extensionTypeUri);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ExtensionBase.cs b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionBase.cs
new file mode 100644
index 0000000..e44bd55
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionBase.cs
@@ -0,0 +1,124 @@
+//-----------------------------------------------------------------------
+// <copyright file="ExtensionBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.OpenId.Messages;
+ using DotNetOpenAuth.Messaging;
+
+ public class ExtensionBase : IOpenIdProtocolMessageExtension {
+ /// <summary>
+ /// Backing store for the <see cref="IOpenIdProtocolMessageExtension.TypeUri"/> property.
+ /// </summary>
+ private string typeUri;
+
+ /// <summary>
+ /// Backing store for the <see cref="IOpenIdProtocolMessageExtension.AdditionalSupportedTypeUris"/> property.
+ /// </summary>
+ private IEnumerable<string> additionalSupportedTypeUris;
+
+ /// <summary>
+ /// Backing store for the <see cref="ExtraData"/> property.
+ /// </summary>
+ private Dictionary<string, string> extraData = new Dictionary<string, string>();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ExtensionBase"/> class.
+ /// </summary>
+ /// <param name="version">The version of the extension.</param>
+ /// <param name="typeUri">The type URI to use in the OpenID message.</param>
+ /// <param name="additionalSupportedTypeUris">The additional supported type URIs by which this extension might be recognized.</param>
+ protected ExtensionBase(Version version, string typeUri, IEnumerable<string> additionalSupportedTypeUris) {
+ this.Version = version;
+ this.typeUri = typeUri;
+ this.additionalSupportedTypeUris = additionalSupportedTypeUris;
+ }
+
+ #region IOpenIdProtocolMessageExtension Members
+
+ /// <summary>
+ /// Gets the TypeURI the extension uses in the OpenID protocol and in XRDS advertisements.
+ /// </summary>
+ string IOpenIdProtocolMessageExtension.TypeUri {
+ get { return typeUri; }
+ }
+
+ /// <summary>
+ /// Additional TypeURIs that are supported by this extension, in preferred order.
+ /// May be empty if none other than <see cref="TypeUri"/> is supported, but
+ /// should not be null.
+ /// </summary>
+ /// <value></value>
+ /// <remarks>
+ /// Useful for reading in messages with an older version of an extension.
+ /// The value in the <see cref="TypeUri"/> property is always checked before
+ /// trying this list.
+ /// If you do support multiple versions of an extension using this method,
+ /// consider adding a CreateResponse method to your request extension class
+ /// so that the response can have the context it needs to remain compatible
+ /// given the version of the extension in the request message.
+ /// The <see cref="SimpleRegistration.ClaimsRequest.CreateResponse"/> for an example.
+ /// </remarks>
+ IEnumerable<string> IOpenIdProtocolMessageExtension.AdditionalSupportedTypeUris {
+ get { return this.additionalSupportedTypeUris; }
+ }
+
+ #endregion
+
+ #region IMessage Members
+
+ /// <summary>
+ /// Gets the version of the protocol or extension this message is prepared to implement.
+ /// </summary>
+ public Version Version { get; private set; }
+
+ /// <summary>
+ /// Gets the extra, non-standard Protocol parameters included in the message.
+ /// </summary>
+ /// <remarks>
+ /// Implementations of this interface should ensure that this property never returns null.
+ /// </remarks>
+ IDictionary<string, string> IMessage.ExtraData {
+ get { return this.extraData; }
+ }
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ void IMessage.EnsureValidMessage() {
+ this.EnsureValidMessage();
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ protected virtual void EnsureValidMessage() {
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/IExtension.cs b/src/DotNetOpenAuth/OpenId/Extensions/IExtension.cs
new file mode 100644
index 0000000..bc4acda
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Extensions/IExtension.cs
@@ -0,0 +1,60 @@
+//-----------------------------------------------------------------------
+// <copyright file="IExtension.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions {
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// The contract an OpenID extension can implement for messages from relying party to provider
+ /// to make handling extensions generally easier.
+ /// Extensions are not required to implement this interface, however.
+ /// </summary>
+ internal interface IExtensionRequest : IOpenIdProtocolMessageExtension {
+ /// <summary>
+ /// Returns the fields this extension should add to an authentication request.
+ /// </summary>
+ IDictionary<string, string> Serialize(RelyingParty.IAuthenticationRequest authenticationRequest);
+
+ /// <summary>
+ /// Reads the extension information on an authentication request to the provider.
+ /// </summary>
+ /// <param name="fields">The fields belonging to the extension.</param>
+ /// <param name="request">The incoming OpenID request carrying the extension.</param>
+ /// <param name="typeUri">The actual extension TypeUri that was recognized in the message.</param>
+ /// <returns>
+ /// True if the extension found a valid set of recognized parameters in the request,
+ /// false otherwise.
+ /// </returns>
+ bool Deserialize(IDictionary<string, string> fields, Provider.IRequest request, string typeUri);
+ }
+
+ /// <summary>
+ /// The contract an OpenID extension can implement for messages from provider to relying party
+ /// to make handling extensions generally easier.
+ /// Extensions are not required to implement this interface, however.
+ /// </summary>
+ internal interface IExtensionResponse : IOpenIdProtocolMessageExtension {
+ /// <summary>
+ /// Returns the fields this extension should add to an authentication response.
+ /// </summary>
+ IDictionary<string, string> Serialize(Provider.IRequest authenticationRequest);
+
+ /// <summary>
+ /// Reads a Provider's response for extension values.
+ /// </summary>
+ /// <param name="fields">The fields belonging to the extension.</param>
+ /// <param name="response">The incoming OpenID response carrying the extension.</param>
+ /// <param name="typeUri">The actual extension TypeUri that was recognized in the message.</param>
+ /// <returns>
+ /// True if the extension found a valid set of recognized parameters in the response,
+ /// false otherwise.
+ /// </returns>
+ bool Deserialize(IDictionary<string, string> fields, RelyingParty.IAuthenticationResponse response, string typeUri);
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/IIncomingExtensions.cs b/src/DotNetOpenAuth/OpenId/Extensions/IIncomingExtensions.cs
new file mode 100644
index 0000000..0b39c5d
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Extensions/IIncomingExtensions.cs
@@ -0,0 +1,30 @@
+//-----------------------------------------------------------------------
+// <copyright file="IIncomingExtensions.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ internal interface IIncomingExtensions {
+ /// <summary>
+ /// Gets the key/value pairs of a provider's response for a given OpenID extension.
+ /// </summary>
+ /// <param name="extensionTypeUri">
+ /// The Type URI of the OpenID extension whose arguments are being sought.
+ /// </param>
+ /// <returns>
+ /// Returns key/value pairs for this extension.
+ /// </returns>
+ IDictionary<string, string> GetExtensionArguments(string extensionTypeUri);
+
+ /// <summary>
+ /// Gets whether any arguments for a given extension are present.
+ /// </summary>
+ bool ContainsExtension(string extensionTypeUri);
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/IOutgoingExtensions.cs b/src/DotNetOpenAuth/OpenId/Extensions/IOutgoingExtensions.cs
new file mode 100644
index 0000000..12cfe0e
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Extensions/IOutgoingExtensions.cs
@@ -0,0 +1,20 @@
+//-----------------------------------------------------------------------
+// <copyright file="IOutgoingExtensions.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ internal interface IOutgoingExtensions {
+ /// <summary>
+ /// Adds query parameters for OpenID extensions to the request directed
+ /// at the OpenID provider.
+ /// </summary>
+ void AddExtensionArguments(string extensionTypeUri, IDictionary<string, string> arguments);
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs
new file mode 100644
index 0000000..79d35fb
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs
@@ -0,0 +1,245 @@
+//-----------------------------------------------------------------------
+// <copyright file="ClaimsRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+ using System.Diagnostics;
+ using System.Globalization;
+ using DotNetOpenAuth.OpenId.Messages;
+ using System.Diagnostics.CodeAnalysis;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// Carries the request/require/none demand state of the simple registration fields.
+ /// </summary>
+#pragma warning disable 0659, 0661
+ [SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals")]
+ public sealed class ClaimsRequest : ExtensionBase {
+ private static readonly string[] additionalTypeUris = new string[] {
+ Constants.sreg_ns10,
+ Constants.sreg_ns11other,
+ };
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ClaimsRequest"/> class.
+ /// </summary>
+ public ClaimsRequest()
+ : base(new Version(1, 0), Constants.sreg_ns, additionalTypeUris) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ClaimsRequest"/> class
+ /// by deserializing from a message.
+ /// </summary>
+ /// <param name="typeUri">The type URI this extension was recognized by in the OpenID message.</param>
+ internal ClaimsRequest(string typeUri) : this() {
+ ErrorUtilities.VerifyNonZeroLength(typeUri, "typeUri");
+
+ this.typeUriDeserializedFrom = typeUri;
+ }
+
+ /// <summary>
+ /// The URL the consumer site provides for the authenticating user to review
+ /// for how his claims will be used by the consumer web site.
+ /// </summary>
+ [MessagePart(Constants.policy_url, IsRequired = false)]
+ public Uri PolicyUrl { get; set; }
+
+ /// <summary>
+ /// The level of interest a relying party has in the nickname of the user.
+ /// </summary>
+ public DemandLevel Nickname { get; set; }
+
+ /// <summary>
+ /// The level of interest a relying party has in the email of the user.
+ /// </summary>
+ public DemandLevel Email { get; set; }
+
+ /// <summary>
+ /// The level of interest a relying party has in the full name of the user.
+ /// </summary>
+ public DemandLevel FullName { get; set; }
+
+ /// <summary>
+ /// The level of interest a relying party has in the birthdate of the user.
+ /// </summary>
+ public DemandLevel BirthDate { get; set; }
+
+ /// <summary>
+ /// The level of interest a relying party has in the gender of the user.
+ /// </summary>
+ public DemandLevel Gender { get; set; }
+
+ /// <summary>
+ /// The level of interest a relying party has in the postal code of the user.
+ /// </summary>
+ public DemandLevel PostalCode { get; set; }
+
+ /// <summary>
+ /// The level of interest a relying party has in the Country of the user.
+ /// </summary>
+ public DemandLevel Country { get; set; }
+
+ /// <summary>
+ /// The level of interest a relying party has in the language of the user.
+ /// </summary>
+ public DemandLevel Language { get; set; }
+
+ /// <summary>
+ /// The level of interest a relying party has in the time zone of the user.
+ /// </summary>
+ public DemandLevel TimeZone { get; set; }
+
+ [MessagePart(Constants.required, AllowEmpty = true)]
+ private string RequiredList {
+ get { return string.Join(",", assembleProfileFields(DemandLevel.Require)); }
+ set { this.SetProfileRequestFromList(value.Split(','), DemandLevel.Require); }
+ }
+
+ [MessagePart(Constants.optional, AllowEmpty = true)]
+ private string OptionalList {
+ get { return string.Join(",", assembleProfileFields(DemandLevel.Request)); }
+ set { this.SetProfileRequestFromList(value.Split(','), DemandLevel.Request); }
+ }
+
+ private string typeUriDeserializedFrom;
+
+ /// <summary>
+ /// Prepares a Simple Registration response extension that is compatible with the
+ /// version of Simple Registration used in the request message.
+ /// </summary>
+ public ClaimsResponse CreateResponse() {
+ if (typeUriDeserializedFrom == null) {
+ throw new InvalidOperationException(OpenIdStrings.CallDeserializeBeforeCreateResponse);
+ }
+ return new ClaimsResponse(typeUriDeserializedFrom);
+ }
+
+ /// <summary>
+ /// Tests equality between two <see cref="ClaimsRequest"/> structs.
+ /// </summary>
+ public static bool operator ==(ClaimsRequest one, ClaimsRequest other) {
+ if ((object)one == null && (object)other == null) return true;
+ if ((object)one == null ^ (object)other == null) return false;
+ return one.Equals(other);
+ }
+
+ /// <summary>
+ /// Tests inequality between two <see cref="ClaimsRequest"/> structs.
+ /// </summary>
+ public static bool operator !=(ClaimsRequest one, ClaimsRequest other) {
+ return !(one == other);
+ }
+
+ /// <summary>
+ /// Tests equality between two <see cref="ClaimsRequest"/> structs.
+ /// </summary>
+ public override bool Equals(object obj) {
+ ClaimsRequest other = obj as ClaimsRequest;
+ if (other == null) return false;
+
+ return
+ this.BirthDate.Equals(other.BirthDate) &&
+ this.Country.Equals(other.Country) &&
+ this.Language.Equals(other.Language) &&
+ this.Email.Equals(other.Email) &&
+ this.FullName.Equals(other.FullName) &&
+ this.Gender.Equals(other.Gender) &&
+ this.Nickname.Equals(other.Nickname) &&
+ this.PostalCode.Equals(other.PostalCode) &&
+ this.TimeZone.Equals(other.TimeZone) &&
+ this.PolicyUrl.EqualsNullSafe(other.PolicyUrl);
+ }
+
+ /// <summary>
+ /// Renders the requested information as a string.
+ /// </summary>
+ public override string ToString() {
+ return string.Format(CultureInfo.CurrentCulture, @"Nickname = '{0}'
+Email = '{1}'
+FullName = '{2}'
+Birthdate = '{3}'
+Gender = '{4}'
+PostalCode = '{5}'
+Country = '{6}'
+Language = '{7}'
+TimeZone = '{8}'", Nickname, Email, FullName, BirthDate, Gender, PostalCode, Country, Language, TimeZone);
+ }
+
+ /// <summary>
+ /// Sets the profile request properties according to a list of
+ /// field names that might have been passed in the OpenId query dictionary.
+ /// </summary>
+ /// <param name="fieldNames">
+ /// The list of field names that should receive a given
+ /// <paramref name="requestLevel"/>. These field names should match
+ /// the OpenId specification for field names, omitting the 'openid.sreg' prefix.
+ /// </param>
+ /// <param name="requestLevel">The none/request/require state of the listed fields.</param>
+ internal void SetProfileRequestFromList(ICollection<string> fieldNames, DemandLevel requestLevel) {
+ foreach (string field in fieldNames) {
+ switch (field) {
+ case Constants.nickname:
+ Nickname = requestLevel;
+ break;
+ case Constants.email:
+ Email = requestLevel;
+ break;
+ case Constants.fullname:
+ FullName = requestLevel;
+ break;
+ case Constants.dob:
+ BirthDate = requestLevel;
+ break;
+ case Constants.gender:
+ Gender = requestLevel;
+ break;
+ case Constants.postcode:
+ PostalCode = requestLevel;
+ break;
+ case Constants.country:
+ Country = requestLevel;
+ break;
+ case Constants.language:
+ Language = requestLevel;
+ break;
+ case Constants.timezone:
+ TimeZone = requestLevel;
+ break;
+ default:
+ Logger.WarnFormat("OpenIdProfileRequest.SetProfileRequestFromList: Unrecognized field name '{0}'.", field);
+ break;
+ }
+ }
+ }
+
+ private string[] assembleProfileFields(DemandLevel level) {
+ List<string> fields = new List<string>(10);
+ if (Nickname == level)
+ fields.Add(Constants.nickname);
+ if (Email == level)
+ fields.Add(Constants.email);
+ if (FullName == level)
+ fields.Add(Constants.fullname);
+ if (BirthDate == level)
+ fields.Add(Constants.dob);
+ if (Gender == level)
+ fields.Add(Constants.gender);
+ if (PostalCode == level)
+ fields.Add(Constants.postcode);
+ if (Country == level)
+ fields.Add(Constants.country);
+ if (Language == level)
+ fields.Add(Constants.language);
+ if (TimeZone == level)
+ fields.Add(Constants.timezone);
+
+ return fields.ToArray();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs
new file mode 100644
index 0000000..2305cea
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsResponse.cs
@@ -0,0 +1,226 @@
+//-----------------------------------------------------------------------
+// <copyright file="ClaimsResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Net.Mail;
+ using System.Text;
+ using System.Xml.Serialization;
+ using DotNetOpenAuth.OpenId.Messages;
+ using System.Diagnostics.CodeAnalysis;
+ using DotNetOpenAuth.Messaging;
+
+#pragma warning disable 0659, 0661
+ /// <summary>
+ /// A struct storing Simple Registration field values describing an
+ /// authenticating user.
+ /// </summary>
+ [SuppressMessage("Microsoft.Usage", "CA2218:OverrideGetHashCodeOnOverridingEquals"), Serializable()]
+ public sealed class ClaimsResponse : ExtensionBase {
+ /// <summary>
+ /// The TypeURI that must be used in the response, based on the one used in the request.
+ /// </summary>
+ private string typeUriToUse;
+
+ /// <summary>
+ /// Backing field for the <see cref="Culture"/> property.
+ /// </summary>
+ private CultureInfo culture;
+
+ /// <summary>
+ /// Creates an instance of the <see cref="ClaimsResponse"/> class.
+ /// </summary>
+ [Obsolete("Use ClaimsRequest.CreateResponse() instead.")]
+ public ClaimsResponse()
+ : this(Constants.sreg_ns) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ClaimsResponse"/> class.
+ /// </summary>
+ /// <param name="typeUriToUse">
+ /// The type URI that must be used to identify this extension in the response message.
+ /// This value should be the same one the relying party used to send the extension request.
+ /// </param>
+ internal ClaimsResponse(string typeUriToUse)
+ : base(new Version(1, 0), typeUriToUse, EmptyList<string>.Instance) {
+ ErrorUtilities.VerifyNonZeroLength(typeUriToUse, "typeUriToUse");
+ this.typeUriToUse = typeUriToUse;
+ }
+
+ /// <summary>
+ /// The nickname the user goes by.
+ /// </summary>
+ [MessagePart(Constants.nickname)]
+ public string Nickname { get; set; }
+
+ /// <summary>
+ /// The user's email address.
+ /// </summary>
+ [MessagePart(Constants.email)]
+ public string Email { get; set; }
+
+ /// <summary>
+ /// The full name of a user as a single string.
+ /// </summary>
+ [MessagePart(Constants.fullname)]
+ public string FullName { get; set; }
+
+ /// <summary>
+ /// The user's birthdate.
+ /// </summary>
+ [MessagePart(Constants.dob)]
+ public DateTime? BirthDate { get; set; }
+
+ /// <summary>
+ /// The gender of the user.
+ /// </summary>
+ [MessagePart(Constants.gender, Encoder = typeof(GenderEncoder))]
+ public Gender? Gender { get; set; }
+
+ /// <summary>
+ /// The zip code / postal code of the user.
+ /// </summary>
+ [MessagePart(Constants.postcode)]
+ public string PostalCode { get; set; }
+
+ /// <summary>
+ /// The country of the user.
+ /// </summary>
+ [MessagePart(Constants.country)]
+ public string Country { get; set; }
+
+ /// <summary>
+ /// The primary/preferred language of the user.
+ /// </summary>
+ [MessagePart(Constants.language)]
+ public string Language { get; set; }
+
+ /// <summary>
+ /// The user's timezone.
+ /// </summary>
+ [MessagePart(Constants.timezone)]
+ public string TimeZone { get; set; }
+
+ /// <summary>
+ /// A combination of the user's full name and email address.
+ /// </summary>
+ public MailAddress MailAddress {
+ get {
+ if (string.IsNullOrEmpty(Email)) return null;
+ if (string.IsNullOrEmpty(FullName))
+ return new MailAddress(Email);
+ else
+ return new MailAddress(Email, FullName);
+ }
+ }
+
+ /// <summary>
+ /// A combination o the language and country of the user.
+ /// </summary>
+ [XmlIgnore]
+ public CultureInfo Culture {
+ get {
+ if (culture == null && !string.IsNullOrEmpty(Language)) {
+ string cultureString = "";
+ cultureString = Language;
+ if (!string.IsNullOrEmpty(Country))
+ cultureString += "-" + Country;
+ culture = CultureInfo.GetCultureInfo(cultureString);
+ }
+
+ return culture;
+ }
+ set {
+ culture = value;
+ Language = (value != null) ? value.TwoLetterISOLanguageName : null;
+ int indexOfHyphen = (value != null) ? value.Name.IndexOf('-') : -1;
+ Country = indexOfHyphen > 0 ? value.Name.Substring(indexOfHyphen + 1) : null;
+ }
+ }
+
+ #region IClientScriptExtension Members
+
+ // TODO: re-enable this
+ ////string IClientScriptExtensionResponse.InitializeJavaScriptData(IDictionary<string, string> sreg, IAuthenticationResponse response, string typeUri) {
+ //// StringBuilder builder = new StringBuilder();
+ //// builder.Append("{ ");
+
+ //// string nickname, email, fullName, dob, genderString, postalCode, country, language, timeZone;
+ //// if (sreg.TryGetValue(Constants.nickname, out nickname)) {
+ //// builder.Append(createAddFieldJS(Constants.nickname, nickname));
+ //// }
+ //// if (sreg.TryGetValue(Constants.email, out email)) {
+ //// builder.Append(createAddFieldJS(Constants.email, email));
+ //// }
+ //// if (sreg.TryGetValue(Constants.fullname, out fullName)) {
+ //// builder.Append(createAddFieldJS(Constants.fullname, fullName));
+ //// }
+ //// if (sreg.TryGetValue(Constants.dob, out dob)) {
+ //// builder.Append(createAddFieldJS(Constants.dob, dob));
+ //// }
+ //// if (sreg.TryGetValue(Constants.gender, out genderString)) {
+ //// builder.Append(createAddFieldJS(Constants.gender, genderString));
+ //// }
+ //// if (sreg.TryGetValue(Constants.postcode, out postalCode)) {
+ //// builder.Append(createAddFieldJS(Constants.postcode, postalCode));
+ //// }
+ //// if (sreg.TryGetValue(Constants.country, out country)) {
+ //// builder.Append(createAddFieldJS(Constants.country, country));
+ //// }
+ //// if (sreg.TryGetValue(Constants.language, out language)) {
+ //// builder.Append(createAddFieldJS(Constants.language, language));
+ //// }
+ //// if (sreg.TryGetValue(Constants.timezone, out timeZone)) {
+ //// builder.Append(createAddFieldJS(Constants.timezone, timeZone));
+ //// }
+ //// if (builder[builder.Length - 1] == ',') builder.Length -= 1;
+ //// builder.Append("}");
+ //// return builder.ToString();
+ ////}
+
+ #endregion
+
+ /// <summary>
+ /// Tests equality of two <see cref="ClaimsResponse"/> objects.
+ /// </summary>
+ public static bool operator ==(ClaimsResponse one, ClaimsResponse other) {
+ return one.EqualsNullSafe(other);
+ }
+
+ /// <summary>
+ /// Tests inequality of two <see cref="ClaimsResponse"/> objects.
+ /// </summary>
+ public static bool operator !=(ClaimsResponse one, ClaimsResponse other) {
+ return !(one == other);
+ }
+
+ /// <summary>
+ /// Tests equality of two <see cref="ClaimsResponse"/> objects.
+ /// </summary>
+ public override bool Equals(object obj) {
+ ClaimsResponse other = obj as ClaimsResponse;
+ if (other == null) return false;
+
+ return
+ this.BirthDate.Equals(other.BirthDate) &&
+ this.Country.EqualsNullSafe(other.Country) &&
+ this.Language.EqualsNullSafe(other.Language) &&
+ this.Email.EqualsNullSafe(other.Email) &&
+ this.FullName.EqualsNullSafe(other.FullName) &&
+ this.Gender.Equals(other.Gender) &&
+ this.Nickname.EqualsNullSafe(other.Nickname) &&
+ this.PostalCode.EqualsNullSafe(other.PostalCode) &&
+ this.TimeZone.EqualsNullSafe(other.TimeZone);
+ }
+
+ private static string createAddFieldJS(string propertyName, string value) {
+ return string.Format(CultureInfo.InvariantCulture, "{0}: {1},", propertyName, Util.GetSafeJavascriptValue(value));
+ }
+ }
+} \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Constants.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Constants.cs
new file mode 100644
index 0000000..85f44de
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Constants.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
+ /// <summary>
+ /// Simple Registration constants
+ /// </summary>
+ internal static class Constants {
+ internal const string sreg_ns = "http://openid.net/extensions/sreg/1.1";
+ internal const string sreg_ns10 = "http://openid.net/sreg/1.0";
+ internal const string sreg_ns11other = "http://openid.net/sreg/1.1";
+ internal const string sreg_compatibility_alias = "sreg";
+ internal const string policy_url = "policy_url";
+ internal const string optional = "optional";
+ internal const string required = "required";
+ internal const string nickname = "nickname";
+ internal const string email = "email";
+ internal const string fullname = "fullname";
+ internal const string dob = "dob";
+ internal const string gender = "gender";
+ internal const string postcode = "postcode";
+ internal const string country = "country";
+ internal const string language = "language";
+ internal const string timezone = "timezone";
+ internal static class Genders {
+ internal const string Male = "M";
+ internal const string Female = "F";
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/DemandLevel.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/DemandLevel.cs
new file mode 100644
index 0000000..16fb57f
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/DemandLevel.cs
@@ -0,0 +1,31 @@
+/********************************************************
+ * Copyright (C) 2007 Andrew Arnott
+ * Released under the New BSD License
+ * License available here: http://www.opensource.org/licenses/bsd-license.php
+ * For news or support on this file: http://blog.nerdbank.net/
+ ********************************************************/
+
+namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
+ /// <summary>
+ /// Specifies what level of interest a relying party has in obtaining the value
+ /// of a given field offered by the Simple Registration extension.
+ /// </summary>
+ public enum DemandLevel {
+ /// <summary>
+ /// The relying party has no interest in obtaining this field.
+ /// </summary>
+ NoRequest,
+ /// <summary>
+ /// The relying party would like the value of this field, but wants
+ /// the Provider to display the field to the user as optionally provided.
+ /// </summary>
+ Request,
+ /// <summary>
+ /// The relying party considers this a required field as part of
+ /// authentication. The Provider and/or user agent MAY still choose to
+ /// not provide the value of the field however, according to the
+ /// Simple Registration extension specification.
+ /// </summary>
+ Require,
+ }
+} \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Gender.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Gender.cs
new file mode 100644
index 0000000..a943179
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/Gender.cs
@@ -0,0 +1,69 @@
+/********************************************************
+ * Copyright (C) 2007 Andrew Arnott
+ * Released under the New BSD License
+ * License available here: http://www.opensource.org/licenses/bsd-license.php
+ * For news or support on this file: http://blog.nerdbank.net/
+ ********************************************************/
+
+using DotNetOpenAuth.Messaging.Reflection;
+using DotNetOpenAuth.Messaging;
+using System;
+namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
+ /// <summary>
+ /// Indicates the gender of a user.
+ /// </summary>
+ public enum Gender {
+ /// <summary>
+ /// The user is male.
+ /// </summary>
+ Male,
+ /// <summary>
+ /// The user is female.
+ /// </summary>
+ Female,
+ }
+
+ /// <summary>
+ /// Encodes/decodes the Simple Registration Gender type to its string representation.
+ /// </summary>
+ internal class GenderEncoder : IMessagePartEncoder {
+ #region IMessagePartEncoder Members
+
+ /// <summary>
+ /// Encodes the specified value.
+ /// </summary>
+ /// <param name="value">The value. Guaranteed to never be null.</param>
+ /// <returns>
+ /// The <paramref name="value"/> in string form, ready for message transport.
+ /// </returns>
+ public string Encode(object value) {
+ Gender? gender = (Gender?)value;
+ if (gender.HasValue) {
+ switch (gender.Value) {
+ case Gender.Male: return Constants.Genders.Male;
+ case Gender.Female: return Constants.Genders.Female;
+ }
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Decodes the specified value.
+ /// </summary>
+ /// <param name="value">The string value carried by the transport. Guaranteed to never be null, although it may be empty.</param>
+ /// <returns>
+ /// The deserialized form of the given string.
+ /// </returns>
+ /// <exception cref="FormatException">Thrown when the string value given cannot be decoded into the required object type.</exception>
+ public object Decode(string value) {
+ switch (value) {
+ case Constants.Genders.Male: return SimpleRegistration.Gender.Male;
+ case Constants.Genders.Female: return SimpleRegistration.Gender.Female;
+ default: throw new FormatException();
+ }
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs b/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs
index bd68111..c4ac4f1 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/AssociateRequest.cs
@@ -1,151 +1,151 @@
-//-----------------------------------------------------------------------
-// <copyright file="AssociateRequest.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Messages {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OpenId.RelyingParty;
-
- /// <summary>
- /// An OpenID direct request from Relying Party to Provider to initiate an association.
- /// </summary>
- [DebuggerDisplay("OpenID {ProtocolVersion} {Mode} {AssociationType} {SessionType}")]
- internal abstract class AssociateRequest : RequestBase {
- /// <summary>
- /// Initializes a new instance of the <see cref="AssociateRequest"/> class.
- /// </summary>
- /// <param name="version">The OpenID version this message must comply with.</param>
- /// <param name="providerEndpoint">The OpenID Provider endpoint.</param>
- protected AssociateRequest(Version version, Uri providerEndpoint)
- : base(version, providerEndpoint, GetProtocolConstant(version, p => p.Args.Mode.associate), MessageTransport.Direct) {
- }
-
- /// <summary>
- /// Gets or sets the preferred association type. The association type defines the algorithm to be used to sign subsequent messages.
- /// </summary>
- /// <value>Value: A valid association type from Section 8.3.</value>
- [MessagePart("openid.assoc_type", IsRequired = true, AllowEmpty = false)]
- internal string AssociationType { get; set; }
-
- /// <summary>
- /// Gets or sets the preferred association session type. This defines the method used to encrypt the association's MAC key in transit.
- /// </summary>
- /// <value>Value: A valid association session type from Section 8.4 (Association Session Types). </value>
- /// <remarks>Note: Unless using transport layer encryption, "no-encryption" MUST NOT be used. </remarks>
- [MessagePart("openid.session_type", IsRequired = true, AllowEmpty = true)]
- [MessagePart("openid.session_type", IsRequired = true, AllowEmpty = false, MinVersion = "2.0")]
- internal string SessionType { get; set; }
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- /// <remarks>
- /// <para>Some messages have required fields, or combinations of fields that must relate to each other
- /// in specialized ways. After deserializing a message, this method checks the state of the
- /// message to see if it conforms to the protocol.</para>
- /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
- /// outside this scope of this particular message.</para>
- /// </remarks>
- /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
- public override void EnsureValidMessage() {
- base.EnsureValidMessage();
-
- ErrorUtilities.VerifyProtocol(
- !string.Equals(this.SessionType, Protocol.Args.SessionType.NoEncryption, StringComparison.Ordinal) || this.Recipient.IsTransportSecure(),
- OpenIdStrings.NoEncryptionSessionRequiresHttps,
- this);
- }
-
- /// <summary>
- /// Creates an association request message that is appropriate for a given Provider.
- /// </summary>
- /// <param name="securityRequirements">The set of requirements the selected association type must comply to.</param>
- /// <param name="provider">The provider to create an association with.</param>
- /// <returns>
- /// The message to send to the Provider to request an association.
- /// Null if no association could be created that meet the security requirements
- /// and the provider OpenID version.
- /// </returns>
- internal static AssociateRequest Create(SecuritySettings securityRequirements, ProviderEndpointDescription provider) {
- ErrorUtilities.VerifyArgumentNotNull(securityRequirements, "securityRequirements");
- ErrorUtilities.VerifyArgumentNotNull(provider, "provider");
-
- AssociateRequest associateRequest;
-
- // Apply our knowledge of the endpoint's transport, OpenID version, and
- // security requirements to decide the best association
- bool unencryptedAllowed = provider.Endpoint.IsTransportSecure();
- bool useDiffieHellman = !unencryptedAllowed;
- string associationType, sessionType;
- if (!HmacShaAssociation.TryFindBestAssociation(Protocol.Lookup(provider.ProtocolVersion), securityRequirements, useDiffieHellman, out associationType, out sessionType)) {
- // There are no associations that meet all requirements.
- Logger.Warn("Security requirements and protocol combination knock out all possible association types. Dumb mode forced.");
- return null;
- }
-
- if (unencryptedAllowed) {
- associateRequest = new AssociateUnencryptedRequest(provider.ProtocolVersion, provider.Endpoint);
- associateRequest.AssociationType = associationType;
- } else {
- var diffieHellmanAssociateRequest = new AssociateDiffieHellmanRequest(provider.ProtocolVersion, provider.Endpoint);
- diffieHellmanAssociateRequest.AssociationType = associationType;
- diffieHellmanAssociateRequest.SessionType = sessionType;
- diffieHellmanAssociateRequest.InitializeRequest();
- associateRequest = diffieHellmanAssociateRequest;
- }
-
- return associateRequest;
- }
-
- /// <summary>
- /// Creates a Provider's response to an incoming association request.
- /// </summary>
- /// <param name="associationStore">The association store where a new association (if created) will be stored. Must not be null.</param>
- /// <returns>
- /// The appropriate association response that is ready to be sent back to the Relying Party.
- /// </returns>
- /// <remarks>
- /// <para>If an association is created, it will be automatically be added to the provided
- /// association store.</para>
- /// <para>Successful association response messages will derive from <see cref="AssociateSuccessfulResponse"/>.
- /// Failed association response messages will derive from <see cref="AssociateUnsuccessfulResponse"/>.</para>
- /// </remarks>
- internal IProtocolMessage CreateResponse(IAssociationStore<AssociationRelyingPartyType> associationStore) {
- ErrorUtilities.VerifyArgumentNotNull(associationStore, "associationStore");
-
- var response = this.CreateResponseCore();
-
- // Create and store the association if this is a successful response.
- var successResponse = response as AssociateSuccessfulResponse;
- if (successResponse != null) {
- Association association = successResponse.CreateAssociation(this);
- associationStore.StoreAssociation(AssociationRelyingPartyType.Smart, association);
- }
-
- return response;
- }
-
- /// <summary>
- /// Creates a Provider's response to an incoming association request.
- /// </summary>
- /// <returns>
- /// The appropriate association response message.
- /// </returns>
- /// <remarks>
- /// <para>If an association can be successfully created, the
- /// <see cref="AssociateSuccessfulResponse.CreateAssociation"/> method must not be
- /// called by this method.</para>
- /// <para>Successful association response messages will derive from <see cref="AssociateSuccessfulResponse"/>.
- /// Failed association response messages will derive from <see cref="AssociateUnsuccessfulResponse"/>.</para>
- /// </remarks>
- protected abstract IProtocolMessage CreateResponseCore();
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="AssociateRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+
+ /// <summary>
+ /// An OpenID direct request from Relying Party to Provider to initiate an association.
+ /// </summary>
+ [DebuggerDisplay("OpenID {ProtocolVersion} {Mode} {AssociationType} {SessionType}")]
+ internal abstract class AssociateRequest : RequestBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AssociateRequest"/> class.
+ /// </summary>
+ /// <param name="version">The OpenID version this message must comply with.</param>
+ /// <param name="providerEndpoint">The OpenID Provider endpoint.</param>
+ protected AssociateRequest(Version version, Uri providerEndpoint)
+ : base(version, providerEndpoint, GetProtocolConstant(version, p => p.Args.Mode.associate), MessageTransport.Direct) {
+ }
+
+ /// <summary>
+ /// Gets or sets the preferred association type. The association type defines the algorithm to be used to sign subsequent messages.
+ /// </summary>
+ /// <value>Value: A valid association type from Section 8.3.</value>
+ [MessagePart("openid.assoc_type", IsRequired = true, AllowEmpty = false)]
+ internal string AssociationType { get; set; }
+
+ /// <summary>
+ /// Gets or sets the preferred association session type. This defines the method used to encrypt the association's MAC key in transit.
+ /// </summary>
+ /// <value>Value: A valid association session type from Section 8.4 (Association Session Types). </value>
+ /// <remarks>Note: Unless using transport layer encryption, "no-encryption" MUST NOT be used. </remarks>
+ [MessagePart("openid.session_type", IsRequired = true, AllowEmpty = true)]
+ [MessagePart("openid.session_type", IsRequired = true, AllowEmpty = false, MinVersion = "2.0")]
+ internal string SessionType { get; set; }
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ public override void EnsureValidMessage() {
+ base.EnsureValidMessage();
+
+ ErrorUtilities.VerifyProtocol(
+ !string.Equals(this.SessionType, Protocol.Args.SessionType.NoEncryption, StringComparison.Ordinal) || this.Recipient.IsTransportSecure(),
+ OpenIdStrings.NoEncryptionSessionRequiresHttps,
+ this);
+ }
+
+ /// <summary>
+ /// Creates an association request message that is appropriate for a given Provider.
+ /// </summary>
+ /// <param name="securityRequirements">The set of requirements the selected association type must comply to.</param>
+ /// <param name="provider">The provider to create an association with.</param>
+ /// <returns>
+ /// The message to send to the Provider to request an association.
+ /// Null if no association could be created that meet the security requirements
+ /// and the provider OpenID version.
+ /// </returns>
+ internal static AssociateRequest Create(SecuritySettings securityRequirements, ProviderEndpointDescription provider) {
+ ErrorUtilities.VerifyArgumentNotNull(securityRequirements, "securityRequirements");
+ ErrorUtilities.VerifyArgumentNotNull(provider, "provider");
+
+ AssociateRequest associateRequest;
+
+ // Apply our knowledge of the endpoint's transport, OpenID version, and
+ // security requirements to decide the best association
+ bool unencryptedAllowed = provider.Endpoint.IsTransportSecure();
+ bool useDiffieHellman = !unencryptedAllowed;
+ string associationType, sessionType;
+ if (!HmacShaAssociation.TryFindBestAssociation(Protocol.Lookup(provider.ProtocolVersion), securityRequirements, useDiffieHellman, out associationType, out sessionType)) {
+ // There are no associations that meet all requirements.
+ Logger.Warn("Security requirements and protocol combination knock out all possible association types. Dumb mode forced.");
+ return null;
+ }
+
+ if (unencryptedAllowed) {
+ associateRequest = new AssociateUnencryptedRequest(provider.ProtocolVersion, provider.Endpoint);
+ associateRequest.AssociationType = associationType;
+ } else {
+ var diffieHellmanAssociateRequest = new AssociateDiffieHellmanRequest(provider.ProtocolVersion, provider.Endpoint);
+ diffieHellmanAssociateRequest.AssociationType = associationType;
+ diffieHellmanAssociateRequest.SessionType = sessionType;
+ diffieHellmanAssociateRequest.InitializeRequest();
+ associateRequest = diffieHellmanAssociateRequest;
+ }
+
+ return associateRequest;
+ }
+
+ /// <summary>
+ /// Creates a Provider's response to an incoming association request.
+ /// </summary>
+ /// <param name="associationStore">The association store where a new association (if created) will be stored. Must not be null.</param>
+ /// <returns>
+ /// The appropriate association response that is ready to be sent back to the Relying Party.
+ /// </returns>
+ /// <remarks>
+ /// <para>If an association is created, it will be automatically be added to the provided
+ /// association store.</para>
+ /// <para>Successful association response messages will derive from <see cref="AssociateSuccessfulResponse"/>.
+ /// Failed association response messages will derive from <see cref="AssociateUnsuccessfulResponse"/>.</para>
+ /// </remarks>
+ internal IProtocolMessage CreateResponse(IAssociationStore<AssociationRelyingPartyType> associationStore) {
+ ErrorUtilities.VerifyArgumentNotNull(associationStore, "associationStore");
+
+ var response = this.CreateResponseCore();
+
+ // Create and store the association if this is a successful response.
+ var successResponse = response as AssociateSuccessfulResponse;
+ if (successResponse != null) {
+ Association association = successResponse.CreateAssociation(this);
+ associationStore.StoreAssociation(AssociationRelyingPartyType.Smart, association);
+ }
+
+ return response;
+ }
+
+ /// <summary>
+ /// Creates a Provider's response to an incoming association request.
+ /// </summary>
+ /// <returns>
+ /// The appropriate association response message.
+ /// </returns>
+ /// <remarks>
+ /// <para>If an association can be successfully created, the
+ /// <see cref="AssociateSuccessfulResponse.CreateAssociation"/> method must not be
+ /// called by this method.</para>
+ /// <para>Successful association response messages will derive from <see cref="AssociateSuccessfulResponse"/>.
+ /// Failed association response messages will derive from <see cref="AssociateUnsuccessfulResponse"/>.</para>
+ /// </remarks>
+ protected abstract IProtocolMessage CreateResponseCore();
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs
index 71da4c8..ae90882 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/CheckAuthenticationRequest.cs
@@ -1,75 +1,75 @@
-//-----------------------------------------------------------------------
-// <copyright file="CheckAuthenticationRequest.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Messages {
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Reflection;
- using DotNetOpenAuth.OpenId.ChannelElements;
-
- /// <summary>
- /// A message a Relying Party sends to a Provider to confirm the validity
- /// of a positive assertion that was signed by a Provider-only secret.
- /// </summary>
- /// <remarks>
- /// The significant payload of this message depends entirely upon the
- /// assertion message, and therefore is all in the
- /// <see cref="DotNetOpenAuth.Messaging.IProtocolMessage.ExtraData"/> property bag.
- /// </remarks>
- internal class CheckAuthenticationRequest : RequestBase {
- /// <summary>
- /// Initializes a new instance of the <see cref="CheckAuthenticationRequest"/> class.
- /// </summary>
- /// <param name="version">The OpenID version this message must comply with.</param>
- /// <param name="providerEndpoint">The OpenID Provider endpoint.</param>
- internal CheckAuthenticationRequest(Version version, Uri providerEndpoint)
- : base(version, providerEndpoint, GetProtocolConstant(version, p => p.Args.Mode.check_authentication), MessageTransport.Direct) {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="CheckAuthenticationRequest"/> class
- /// based on the contents of some signed message whose signature must be verified.
- /// </summary>
- /// <param name="message">The message whose signature should be verified.</param>
- internal CheckAuthenticationRequest(IndirectSignedResponse message)
- : base(message.ProtocolVersion, message.ProviderEndpoint, GetProtocolConstant(message.ProtocolVersion, p => p.Args.Mode.check_authentication), MessageTransport.Direct) {
- // Copy all message parts from the id_res message into this one,
- // except for the openid.mode parameter.
- MessageDictionary checkPayload = new MessageDictionary(message);
- MessageDictionary thisPayload = new MessageDictionary(this);
- foreach (var pair in checkPayload) {
- if (!string.Equals(pair.Key, this.Protocol.openid.mode)) {
- thisPayload[pair.Key] = pair.Value;
- }
- }
- }
-
- /// <summary>
- /// Gets or sets a value indicating whether the signature being verified by this request
- /// is in fact valid.
- /// </summary>
- /// <value><c>true</c> if the signature is valid; otherwise, <c>false</c>.</value>
- /// <remarks>
- /// This property is automatically set as the message is received by the channel's
- /// signing binding element.
- /// </remarks>
- internal bool IsValid { get; set; }
-
- /// <summary>
- /// Gets or sets the ReturnTo that existed in the original signed message.
- /// </summary>
- /// <remarks>
- /// This exists strictly for convenience in recreating the <see cref="IndirectSignedResponse"/>
- /// message.
- /// </remarks>
- [MessagePart("openid.return_to", IsRequired = true, AllowEmpty = false)]
- [MessagePart("openid.return_to", IsRequired = false, AllowEmpty = false, MinVersion = "2.0")]
- internal Uri ReturnTo { get; set; }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="CheckAuthenticationRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Reflection;
+ using DotNetOpenAuth.OpenId.ChannelElements;
+
+ /// <summary>
+ /// A message a Relying Party sends to a Provider to confirm the validity
+ /// of a positive assertion that was signed by a Provider-only secret.
+ /// </summary>
+ /// <remarks>
+ /// The significant payload of this message depends entirely upon the
+ /// assertion message, and therefore is all in the
+ /// <see cref="DotNetOpenAuth.Messaging.IMessage.ExtraData"/> property bag.
+ /// </remarks>
+ internal class CheckAuthenticationRequest : RequestBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CheckAuthenticationRequest"/> class.
+ /// </summary>
+ /// <param name="version">The OpenID version this message must comply with.</param>
+ /// <param name="providerEndpoint">The OpenID Provider endpoint.</param>
+ internal CheckAuthenticationRequest(Version version, Uri providerEndpoint)
+ : base(version, providerEndpoint, GetProtocolConstant(version, p => p.Args.Mode.check_authentication), MessageTransport.Direct) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CheckAuthenticationRequest"/> class
+ /// based on the contents of some signed message whose signature must be verified.
+ /// </summary>
+ /// <param name="message">The message whose signature should be verified.</param>
+ internal CheckAuthenticationRequest(IndirectSignedResponse message)
+ : base(message.Version, message.ProviderEndpoint, GetProtocolConstant(message.Version, p => p.Args.Mode.check_authentication), MessageTransport.Direct) {
+ // Copy all message parts from the id_res message into this one,
+ // except for the openid.mode parameter.
+ MessageDictionary checkPayload = new MessageDictionary(message);
+ MessageDictionary thisPayload = new MessageDictionary(this);
+ foreach (var pair in checkPayload) {
+ if (!string.Equals(pair.Key, this.Protocol.openid.mode)) {
+ thisPayload[pair.Key] = pair.Value;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether the signature being verified by this request
+ /// is in fact valid.
+ /// </summary>
+ /// <value><c>true</c> if the signature is valid; otherwise, <c>false</c>.</value>
+ /// <remarks>
+ /// This property is automatically set as the message is received by the channel's
+ /// signing binding element.
+ /// </remarks>
+ internal bool IsValid { get; set; }
+
+ /// <summary>
+ /// Gets or sets the ReturnTo that existed in the original signed message.
+ /// </summary>
+ /// <remarks>
+ /// This exists strictly for convenience in recreating the <see cref="IndirectSignedResponse"/>
+ /// message.
+ /// </remarks>
+ [MessagePart("openid.return_to", IsRequired = true, AllowEmpty = false)]
+ [MessagePart("openid.return_to", IsRequired = false, AllowEmpty = false, MinVersion = "2.0")]
+ internal Uri ReturnTo { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/DirectResponseBase.cs b/src/DotNetOpenAuth/OpenId/Messages/DirectResponseBase.cs
index ed83ff4..fd0a974 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/DirectResponseBase.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/DirectResponseBase.cs
@@ -1,155 +1,155 @@
-//-----------------------------------------------------------------------
-// <copyright file="DirectResponseBase.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Messages {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Diagnostics.CodeAnalysis;
- using DotNetOpenAuth.Messaging;
-
- /// <summary>
- /// A common base class for OpenID direct message responses.
- /// </summary>
- [DebuggerDisplay("OpenID {ProtocolVersion} response")]
- internal class DirectResponseBase : IDirectResponseProtocolMessage {
- /// <summary>
- /// The openid.ns parameter in the message.
- /// </summary>
- /// <value>"http://specs.openid.net/auth/2.0" </value>
- /// <remarks>
- /// OpenID 2.0 Section 5.1.2:
- /// This particular value MUST be present for the response to be a valid OpenID 2.0 response.
- /// Future versions of the specification may define different values in order to allow message
- /// recipients to properly interpret the request.
- /// </remarks>
- [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Read by reflection.")]
- [MessagePart("ns", IsRequired = true, AllowEmpty = false)]
-#pragma warning disable 0414 // read by reflection
- private readonly string OpenIdNamespace = Protocol.OpenId2Namespace;
-#pragma warning restore 0414
-
- /// <summary>
- /// Backing store for the <see cref="OriginatingRequest"/> properties.
- /// </summary>
- private IDirectedProtocolMessage originatingRequest;
-
- /// <summary>
- /// Backing store for the <see cref="Incoming"/> properties.
- /// </summary>
- private bool incoming;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="DirectResponseBase"/> class.
- /// </summary>
- /// <param name="originatingRequest">The originating request.</param>
- protected DirectResponseBase(IDirectedProtocolMessage originatingRequest) {
- ErrorUtilities.VerifyArgumentNotNull(originatingRequest, "originatingRequest");
-
- this.originatingRequest = originatingRequest;
- this.ProtocolVersion = originatingRequest.ProtocolVersion;
- }
-
- #region IProtocolMessage Properties
-
- /// <summary>
- /// Gets the version of the protocol this message is prepared to implement.
- /// </summary>
- /// <value>Version 2.0</value>
- public Version ProtocolVersion { get; private set; }
-
- /// <summary>
- /// Gets the level of protection this message requires.
- /// </summary>
- /// <value><see cref="MessageProtections.None"/></value>
- public MessageProtections RequiredProtection {
- get { return MessageProtections.None; }
- }
-
- /// <summary>
- /// Gets a value indicating whether this is a direct or indirect message.
- /// </summary>
- /// <value><see cref="MessageTransport.Direct"/></value>
- public MessageTransport Transport {
- get { return MessageTransport.Direct; }
- }
-
- /// <summary>
- /// Gets the extra, non-OAuth parameters included in the message.
- /// </summary>
- /// <value>An empty dictionary.</value>
- public IDictionary<string, string> ExtraData {
- get { return EmptyDictionary<string, string>.Instance; }
- }
-
- /// <summary>
- /// Gets a value indicating whether this message was deserialized as an incoming message.
- /// </summary>
- bool IProtocolMessage.Incoming {
- get { return this.incoming; }
- }
-
- #endregion
-
- #region IDirectResponseProtocolMessage Members
-
- /// <summary>
- /// Gets the originating request message that caused this response to be formed.
- /// </summary>
- IDirectedProtocolMessage IDirectResponseProtocolMessage.OriginatingRequest {
- get { return this.originatingRequest; }
- }
-
- #endregion
-
- /// <summary>
- /// Gets the protocol used by this message.
- /// </summary>
- protected Protocol Protocol {
- get { return Protocol.Lookup(this.ProtocolVersion); }
- }
-
- /// <summary>
- /// Gets the originating request message that caused this response to be formed.
- /// </summary>
- protected IDirectedProtocolMessage OriginatingRequest {
- get { return this.originatingRequest; }
- }
-
- /// <summary>
- /// Gets a value indicating whether this message was deserialized as an incoming message.
- /// </summary>
- protected bool Incoming {
- get { return this.incoming; }
- }
-
- #region IProtocolMessage methods
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- /// <remarks>
- /// <para>Some messages have required fields, or combinations of fields that must relate to each other
- /// in specialized ways. After deserializing a message, this method checks the state of the
- /// message to see if it conforms to the protocol.</para>
- /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
- /// outside this scope of this particular message.</para>
- /// </remarks>
- /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
- public virtual void EnsureValidMessage() {
- }
-
- #endregion
-
- /// <summary>
- /// Sets a flag indicating that this message is received (as opposed to sent).
- /// </summary>
- internal void SetAsIncoming() {
- this.incoming = true;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="DirectResponseBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A common base class for OpenID direct message responses.
+ /// </summary>
+ [DebuggerDisplay("OpenID {ProtocolVersion} response")]
+ internal class DirectResponseBase : IDirectResponseProtocolMessage {
+ /// <summary>
+ /// The openid.ns parameter in the message.
+ /// </summary>
+ /// <value>"http://specs.openid.net/auth/2.0" </value>
+ /// <remarks>
+ /// OpenID 2.0 Section 5.1.2:
+ /// This particular value MUST be present for the response to be a valid OpenID 2.0 response.
+ /// Future versions of the specification may define different values in order to allow message
+ /// recipients to properly interpret the request.
+ /// </remarks>
+ [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Read by reflection.")]
+ [MessagePart("ns", IsRequired = true, AllowEmpty = false)]
+#pragma warning disable 0414 // read by reflection
+ private readonly string OpenIdNamespace = Protocol.OpenId2Namespace;
+#pragma warning restore 0414
+
+ /// <summary>
+ /// Backing store for the <see cref="OriginatingRequest"/> properties.
+ /// </summary>
+ private IDirectedProtocolMessage originatingRequest;
+
+ /// <summary>
+ /// Backing store for the <see cref="Incoming"/> properties.
+ /// </summary>
+ private bool incoming;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DirectResponseBase"/> class.
+ /// </summary>
+ /// <param name="originatingRequest">The originating request.</param>
+ protected DirectResponseBase(IDirectedProtocolMessage originatingRequest) {
+ ErrorUtilities.VerifyArgumentNotNull(originatingRequest, "originatingRequest");
+
+ this.originatingRequest = originatingRequest;
+ this.Version = originatingRequest.Version;
+ }
+
+ #region IProtocolMessage Properties
+
+ /// <summary>
+ /// Gets the version of the protocol this message is prepared to implement.
+ /// </summary>
+ /// <value>Version 2.0</value>
+ public Version Version { get; private set; }
+
+ /// <summary>
+ /// Gets the level of protection this message requires.
+ /// </summary>
+ /// <value><see cref="MessageProtections.None"/></value>
+ public MessageProtections RequiredProtection {
+ get { return MessageProtections.None; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this is a direct or indirect message.
+ /// </summary>
+ /// <value><see cref="MessageTransport.Direct"/></value>
+ public MessageTransport Transport {
+ get { return MessageTransport.Direct; }
+ }
+
+ /// <summary>
+ /// Gets the extra, non-OAuth parameters included in the message.
+ /// </summary>
+ /// <value>An empty dictionary.</value>
+ public IDictionary<string, string> ExtraData {
+ get { return EmptyDictionary<string, string>.Instance; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this message was deserialized as an incoming message.
+ /// </summary>
+ bool IMessage.Incoming {
+ get { return this.incoming; }
+ }
+
+ #endregion
+
+ #region IDirectResponseProtocolMessage Members
+
+ /// <summary>
+ /// Gets the originating request message that caused this response to be formed.
+ /// </summary>
+ IDirectedProtocolMessage IDirectResponseProtocolMessage.OriginatingRequest {
+ get { return this.originatingRequest; }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets the protocol used by this message.
+ /// </summary>
+ protected Protocol Protocol {
+ get { return Protocol.Lookup(this.Version); }
+ }
+
+ /// <summary>
+ /// Gets the originating request message that caused this response to be formed.
+ /// </summary>
+ protected IDirectedProtocolMessage OriginatingRequest {
+ get { return this.originatingRequest; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this message was deserialized as an incoming message.
+ /// </summary>
+ protected bool Incoming {
+ get { return this.incoming; }
+ }
+
+ #region IProtocolMessage methods
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ public virtual void EnsureValidMessage() {
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Sets a flag indicating that this message is received (as opposed to sent).
+ /// </summary>
+ internal void SetAsIncoming() {
+ this.incoming = true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/IOpenIdProtocolMessageExtension.cs b/src/DotNetOpenAuth/OpenId/Messages/IOpenIdProtocolMessageExtension.cs
new file mode 100644
index 0000000..5b5d69f
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Messages/IOpenIdProtocolMessageExtension.cs
@@ -0,0 +1,40 @@
+//-----------------------------------------------------------------------
+// <copyright file="IOpenIdProtocolMessageExtension.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// The contract any OpenID extension for DotNetOpenId must implement.
+ /// </summary>
+ internal interface IOpenIdProtocolMessageExtension : IExtensionMessage {
+ /// <summary>
+ /// Gets the TypeURI the extension uses in the OpenID protocol and in XRDS advertisements.
+ /// </summary>
+ string TypeUri { get; }
+
+ /// <summary>
+ /// Additional TypeURIs that are supported by this extension, in preferred order.
+ /// May be empty if none other than <see cref="TypeUri"/> is supported, but
+ /// should not be null.
+ /// </summary>
+ /// <remarks>
+ /// Useful for reading in messages with an older version of an extension.
+ /// The value in the <see cref="TypeUri"/> property is always checked before
+ /// trying this list.
+ /// If you do support multiple versions of an extension using this method,
+ /// consider adding a CreateResponse method to your request extension class
+ /// so that the response can have the context it needs to remain compatible
+ /// given the version of the extension in the request message.
+ /// The <see cref="SimpleRegistration.ClaimsRequest.CreateResponse"/> for an example.
+ /// </remarks>
+ IEnumerable<string> AdditionalSupportedTypeUris { get; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/IndirectResponseBase.cs b/src/DotNetOpenAuth/OpenId/Messages/IndirectResponseBase.cs
index 2aaff15..2644f44 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/IndirectResponseBase.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/IndirectResponseBase.cs
@@ -1,77 +1,77 @@
-//-----------------------------------------------------------------------
-// <copyright file="IndirectResponseBase.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Messages {
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging;
-
- /// <summary>
- /// A common base class from which indirect response messages should derive.
- /// </summary>
- internal class IndirectResponseBase : RequestBase {
- /// <summary>
- /// Initializes a new instance of the <see cref="IndirectResponseBase"/> class.
- /// </summary>
- /// <param name="request">The request that caused this response message to be constructed.</param>
- /// <param name="mode">The value of the openid.mode parameter.</param>
- protected IndirectResponseBase(SignedResponseRequest request, string mode)
- : base(GetVersion(request), GetReturnTo(request), mode, MessageTransport.Indirect) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
-
- this.OriginatingRequest = request;
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="IndirectResponseBase"/> class
- /// for unsolicited assertion scenarios.
- /// </summary>
- /// <param name="version">The OpenID version supported at the Relying Party.</param>
- /// <param name="relyingPartyReturnTo">
- /// The URI at which the Relying Party receives OpenID indirect messages.
- /// </param>
- /// <param name="mode">The value to use for the openid.mode parameter.</param>
- protected IndirectResponseBase(Version version, Uri relyingPartyReturnTo, string mode)
- : base(version, relyingPartyReturnTo, mode, MessageTransport.Indirect) {
- }
-
- /// <summary>
- /// Gets the originating request message, if applicable.
- /// </summary>
- protected SignedResponseRequest OriginatingRequest { get; private set; }
-
- /// <summary>
- /// Gets the <see cref="IProtocolMessage.ProtocolVersion"/> property of a message.
- /// </summary>
- /// <param name="message">The message to fetch the protocol version from.</param>
- /// <returns>The value of the <see cref="IProtocolMessage.ProtocolVersion"/> property.</returns>
- /// <remarks>
- /// This method can be used by a constructor to throw an <see cref="ArgumentNullException"/>
- /// instead of a <see cref="NullReferenceException"/>.
- /// </remarks>
- protected static Version GetVersion(IProtocolMessage message) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
- return message.ProtocolVersion;
- }
-
- /// <summary>
- /// Gets the <see cref="SignedResponseRequest.ReturnTo"/> property of a message.
- /// </summary>
- /// <param name="message">The message to fetch the ReturnTo from.</param>
- /// <returns>The value of the <see cref="SignedResponseRequest.ReturnTo"/> property.</returns>
- /// <remarks>
- /// This method can be used by a constructor to throw an <see cref="ArgumentNullException"/>
- /// instead of a <see cref="NullReferenceException"/>.
- /// </remarks>
- private static Uri GetReturnTo(SignedResponseRequest message) {
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
- ErrorUtilities.VerifyProtocol(message.ReturnTo != null, OpenIdStrings.ReturnToRequiredForResponse);
- return message.ReturnTo;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="IndirectResponseBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A common base class from which indirect response messages should derive.
+ /// </summary>
+ internal class IndirectResponseBase : RequestBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="IndirectResponseBase"/> class.
+ /// </summary>
+ /// <param name="request">The request that caused this response message to be constructed.</param>
+ /// <param name="mode">The value of the openid.mode parameter.</param>
+ protected IndirectResponseBase(SignedResponseRequest request, string mode)
+ : base(GetVersion(request), GetReturnTo(request), mode, MessageTransport.Indirect) {
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+
+ this.OriginatingRequest = request;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="IndirectResponseBase"/> class
+ /// for unsolicited assertion scenarios.
+ /// </summary>
+ /// <param name="version">The OpenID version supported at the Relying Party.</param>
+ /// <param name="relyingPartyReturnTo">
+ /// The URI at which the Relying Party receives OpenID indirect messages.
+ /// </param>
+ /// <param name="mode">The value to use for the openid.mode parameter.</param>
+ protected IndirectResponseBase(Version version, Uri relyingPartyReturnTo, string mode)
+ : base(version, relyingPartyReturnTo, mode, MessageTransport.Indirect) {
+ }
+
+ /// <summary>
+ /// Gets the originating request message, if applicable.
+ /// </summary>
+ protected SignedResponseRequest OriginatingRequest { get; private set; }
+
+ /// <summary>
+ /// Gets the <see cref="IMessage.Version"/> property of a message.
+ /// </summary>
+ /// <param name="message">The message to fetch the protocol version from.</param>
+ /// <returns>The value of the <see cref="IMessage.Version"/> property.</returns>
+ /// <remarks>
+ /// This method can be used by a constructor to throw an <see cref="ArgumentNullException"/>
+ /// instead of a <see cref="NullReferenceException"/>.
+ /// </remarks>
+ protected static Version GetVersion(IProtocolMessage message) {
+ ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ return message.Version;
+ }
+
+ /// <summary>
+ /// Gets the <see cref="SignedResponseRequest.ReturnTo"/> property of a message.
+ /// </summary>
+ /// <param name="message">The message to fetch the ReturnTo from.</param>
+ /// <returns>The value of the <see cref="SignedResponseRequest.ReturnTo"/> property.</returns>
+ /// <remarks>
+ /// This method can be used by a constructor to throw an <see cref="ArgumentNullException"/>
+ /// instead of a <see cref="NullReferenceException"/>.
+ /// </remarks>
+ private static Uri GetReturnTo(SignedResponseRequest message) {
+ ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ ErrorUtilities.VerifyProtocol(message.ReturnTo != null, OpenIdStrings.ReturnToRequiredForResponse);
+ return message.ReturnTo;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/NegativeAssertionResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/NegativeAssertionResponse.cs
index f094e14..0c9e05a 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/NegativeAssertionResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/NegativeAssertionResponse.cs
@@ -1,104 +1,104 @@
-//-----------------------------------------------------------------------
-// <copyright file="NegativeAssertionResponse.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Messages {
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging;
-
- /// <summary>
- /// The message OpenID Providers send back to Relying Parties to refuse
- /// to assert the identity of a user.
- /// </summary>
- internal class NegativeAssertionResponse : IndirectResponseBase {
- /// <summary>
- /// Initializes a new instance of the <see cref="NegativeAssertionResponse"/> class.
- /// </summary>
- /// <param name="request">The request that the relying party sent.</param>
- internal NegativeAssertionResponse(CheckIdRequest request)
- : base(request, GetMode(request)) {
- }
-
- /// <summary>
- /// Initializes a new instance of the <see cref="NegativeAssertionResponse"/> class.
- /// </summary>
- /// <param name="version">The version.</param>
- /// <param name="relyingPartyReturnTo">The relying party return to.</param>
- /// <param name="mode">The value of the openid.mode parameter.</param>
- internal NegativeAssertionResponse(Version version, Uri relyingPartyReturnTo, string mode)
- : base(version, relyingPartyReturnTo, mode) {
- }
-
- /// <summary>
- /// Gets or sets the URL the relying party can use to upgrade their authentication
- /// request from an immediate to a setup message.
- /// </summary>
- /// <value>URL to redirect User-Agent to so the End User can do whatever's necessary to fulfill the assertion.</value>
- /// <remarks>
- /// This part is only included
- /// </remarks>
- [MessagePart("openid.user_setup_url", AllowEmpty = false, IsRequired = false, MaxVersion = "1.1")]
- internal Uri UserSetupUrl { get; set; }
-
- /// <summary>
- /// Gets a value indicating whether this <see cref="NegativeAssertionResponse"/>
- /// is in response to an authentication request made in immediate mode.
- /// </summary>
- /// <value><c>true</c> if the request was in immediate mode; otherwise, <c>false</c>.</value>
- internal bool Immediate {
- get {
- if (this.OriginatingRequest != null) {
- return this.OriginatingRequest.Immediate;
- } else {
- if (String.Equals(this.Mode, Protocol.Args.Mode.setup_needed, StringComparison.Ordinal)) {
- return true;
- } else if (String.Equals(this.Mode, Protocol.Args.Mode.cancel, StringComparison.Ordinal)) {
- return false;
- } else {
- throw ErrorUtilities.ThrowProtocol(MessagingStrings.UnexpectedMessagePartValue, Protocol.openid.mode, this.Mode);
- }
- }
- }
- }
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- /// <remarks>
- /// <para>Some messages have required fields, or combinations of fields that must relate to each other
- /// in specialized ways. After deserializing a message, this method checks the state of the
- /// message to see if it conforms to the protocol.</para>
- /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
- /// outside this scope of this particular message.</para>
- /// </remarks>
- /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
- public override void EnsureValidMessage() {
- base.EnsureValidMessage();
-
- // Since there are a couple of negative assertion modes, ensure that the mode given is one of the allowed ones.
- ErrorUtilities.VerifyProtocol(String.Equals(this.Mode, Protocol.Args.Mode.setup_needed, StringComparison.Ordinal) || String.Equals(this.Mode, Protocol.Args.Mode.cancel, StringComparison.Ordinal), MessagingStrings.UnexpectedMessagePartValue, Protocol.openid.mode, this.Mode);
-
- if (this.Immediate && Protocol.Version.Major < 2) {
- ErrorUtilities.VerifyProtocol(this.UserSetupUrl != null, OpenIdStrings.UserSetupUrlRequiredInImmediateNegativeResponse);
- }
- }
-
- /// <summary>
- /// Gets the value for the openid.mode that is appropriate for this response.
- /// </summary>
- /// <param name="request">The request that we're responding to.</param>
- /// <returns>The value of the openid.mode parameter to use.</returns>
- private static string GetMode(CheckIdRequest request) {
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
-
- Protocol protocol = Protocol.Lookup(request.ProtocolVersion);
- return request.Immediate ? protocol.Args.Mode.setup_needed : protocol.Args.Mode.cancel;
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="NegativeAssertionResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// The message OpenID Providers send back to Relying Parties to refuse
+ /// to assert the identity of a user.
+ /// </summary>
+ internal class NegativeAssertionResponse : IndirectResponseBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NegativeAssertionResponse"/> class.
+ /// </summary>
+ /// <param name="request">The request that the relying party sent.</param>
+ internal NegativeAssertionResponse(CheckIdRequest request)
+ : base(request, GetMode(request)) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NegativeAssertionResponse"/> class.
+ /// </summary>
+ /// <param name="version">The version.</param>
+ /// <param name="relyingPartyReturnTo">The relying party return to.</param>
+ /// <param name="mode">The value of the openid.mode parameter.</param>
+ internal NegativeAssertionResponse(Version version, Uri relyingPartyReturnTo, string mode)
+ : base(version, relyingPartyReturnTo, mode) {
+ }
+
+ /// <summary>
+ /// Gets or sets the URL the relying party can use to upgrade their authentication
+ /// request from an immediate to a setup message.
+ /// </summary>
+ /// <value>URL to redirect User-Agent to so the End User can do whatever's necessary to fulfill the assertion.</value>
+ /// <remarks>
+ /// This part is only included
+ /// </remarks>
+ [MessagePart("openid.user_setup_url", AllowEmpty = false, IsRequired = false, MaxVersion = "1.1")]
+ internal Uri UserSetupUrl { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether this <see cref="NegativeAssertionResponse"/>
+ /// is in response to an authentication request made in immediate mode.
+ /// </summary>
+ /// <value><c>true</c> if the request was in immediate mode; otherwise, <c>false</c>.</value>
+ internal bool Immediate {
+ get {
+ if (this.OriginatingRequest != null) {
+ return this.OriginatingRequest.Immediate;
+ } else {
+ if (String.Equals(this.Mode, Protocol.Args.Mode.setup_needed, StringComparison.Ordinal)) {
+ return true;
+ } else if (String.Equals(this.Mode, Protocol.Args.Mode.cancel, StringComparison.Ordinal)) {
+ return false;
+ } else {
+ throw ErrorUtilities.ThrowProtocol(MessagingStrings.UnexpectedMessagePartValue, Protocol.openid.mode, this.Mode);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ public override void EnsureValidMessage() {
+ base.EnsureValidMessage();
+
+ // Since there are a couple of negative assertion modes, ensure that the mode given is one of the allowed ones.
+ ErrorUtilities.VerifyProtocol(String.Equals(this.Mode, Protocol.Args.Mode.setup_needed, StringComparison.Ordinal) || String.Equals(this.Mode, Protocol.Args.Mode.cancel, StringComparison.Ordinal), MessagingStrings.UnexpectedMessagePartValue, Protocol.openid.mode, this.Mode);
+
+ if (this.Immediate && Protocol.Version.Major < 2) {
+ ErrorUtilities.VerifyProtocol(this.UserSetupUrl != null, OpenIdStrings.UserSetupUrlRequiredInImmediateNegativeResponse);
+ }
+ }
+
+ /// <summary>
+ /// Gets the value for the openid.mode that is appropriate for this response.
+ /// </summary>
+ /// <param name="request">The request that we're responding to.</param>
+ /// <returns>The value of the openid.mode parameter to use.</returns>
+ private static string GetMode(CheckIdRequest request) {
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+
+ Protocol protocol = Protocol.Lookup(request.Version);
+ return request.Immediate ? protocol.Args.Mode.setup_needed : protocol.Args.Mode.cancel;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs b/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs
index 6786a44..b7f0536 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/RequestBase.cs
@@ -1,193 +1,193 @@
-//-----------------------------------------------------------------------
-// <copyright file="RequestBase.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OpenId.Messages {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Diagnostics.CodeAnalysis;
- using DotNetOpenAuth.Messaging;
-
- /// <summary>
- /// A common base class for OpenID request messages and indirect responses (since they are ultimately requests).
- /// </summary>
- [DebuggerDisplay("OpenID {ProtocolVersion} {Mode}")]
- internal class RequestBase : IDirectedProtocolMessage {
- /// <summary>
- /// The openid.ns parameter in the message.
- /// </summary>
- /// <value>"http://specs.openid.net/auth/2.0" </value>
- /// <remarks>
- /// This particular value MUST be present for the request to be a valid OpenID Authentication 2.0 request. Future versions of the specification may define different values in order to allow message recipients to properly interpret the request.
- /// </remarks>
- [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Read by reflection.")]
- [MessagePart("openid.ns", IsRequired = true, AllowEmpty = false, MinVersion = "2.0")]
-#pragma warning disable 0414 // read by reflection
- private readonly string OpenIdNamespace = Protocol.OpenId2Namespace;
-#pragma warning restore 0414
-
- /// <summary>
- /// Backing store for the <see cref="Incoming"/> property.
- /// </summary>
- private bool incoming;
-
- /// <summary>
- /// Backing store for the <see cref="ExtraData"/> property.
- /// </summary>
- private Dictionary<string, string> extraData = new Dictionary<string, string>();
-
- /// <summary>
- /// Initializes a new instance of the <see cref="RequestBase"/> class.
- /// </summary>
- /// <param name="version">The OpenID version this message must comply with.</param>
- /// <param name="providerEndpoint">The OpenID Provider endpoint.</param>
- /// <param name="mode">The value for the openid.mode parameter.</param>
- /// <param name="transport">A value indicating whether the message will be transmitted directly or indirectly.</param>
- protected RequestBase(Version version, Uri providerEndpoint, string mode, MessageTransport transport) {
- if (providerEndpoint == null) {
- throw new ArgumentNullException("providerEndpoint");
- }
- if (String.IsNullOrEmpty(mode)) {
- throw new ArgumentNullException("mode");
- }
-
- this.Recipient = providerEndpoint;
- this.Mode = mode;
- this.Transport = transport;
- this.ProtocolVersion = version;
- }
-
- /// <summary>
- /// Gets the value of the openid.mode parameter.
- /// </summary>
- [MessagePart("openid.mode", IsRequired = true, AllowEmpty = false)]
- public string Mode { get; private set; }
-
- #region IDirectedProtocolMessage Members
-
- /// <summary>
- /// Gets the preferred method of transport for the message.
- /// </summary>
- /// <value>
- /// For direct messages this is the OpenID mandated POST.
- /// For indirect messages both GET and POST are allowed.
- /// </value>
- HttpDeliveryMethods IDirectedProtocolMessage.HttpMethods {
- get {
- HttpDeliveryMethods methods = HttpDeliveryMethods.PostRequest;
- if (this.Transport == MessageTransport.Indirect) {
- methods |= HttpDeliveryMethods.GetRequest;
- }
- return methods;
- }
- }
-
- /// <summary>
- /// Gets the recipient of the message.
- /// </summary>
- /// <value>The OP endpoint, or the RP return_to.</value>
- public Uri Recipient {
- get;
- private set;
- }
-
- #endregion
-
- #region IProtocolMessage Properties
-
- /// <summary>
- /// Gets the version of the protocol this message is prepared to implement.
- /// </summary>
- /// <value>Version 2.0</value>
- public Version ProtocolVersion { get; private set; }
-
- /// <summary>
- /// Gets the level of protection this message requires.
- /// </summary>
- /// <value><see cref="MessageProtections.None"/></value>
- public virtual MessageProtections RequiredProtection {
- get { return MessageProtections.None; }
- }
-
- /// <summary>
- /// Gets a value indicating whether this is a direct or indirect message.
- /// </summary>
- /// <value><see cref="MessageTransport.Direct"/></value>
- public MessageTransport Transport { get; private set; }
-
- /// <summary>
- /// Gets the extra parameters included in the message.
- /// </summary>
- /// <value>An empty dictionary.</value>
- public IDictionary<string, string> ExtraData {
- get { return this.extraData; }
- }
-
- /// <summary>
- /// Gets a value indicating whether this message was deserialized as an incoming message.
- /// </summary>
- bool IProtocolMessage.Incoming {
- get { return this.incoming; }
- }
-
- #endregion
-
- /// <summary>
- /// Gets the protocol used by this message.
- /// </summary>
- protected Protocol Protocol {
- get { return Protocol.Lookup(this.ProtocolVersion); }
- }
-
- /// <summary>
- /// Gets a value indicating whether this message was deserialized as an incoming message.
- /// </summary>
- protected bool Incoming {
- get { return this.incoming; }
- }
-
- #region IProtocolMessage Methods
-
- /// <summary>
- /// Checks the message state for conformity to the protocol specification
- /// and throws an exception if the message is invalid.
- /// </summary>
- /// <remarks>
- /// <para>Some messages have required fields, or combinations of fields that must relate to each other
- /// in specialized ways. After deserializing a message, this method checks the state of the
- /// message to see if it conforms to the protocol.</para>
- /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
- /// outside this scope of this particular message.</para>
- /// </remarks>
- /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
- public virtual void EnsureValidMessage() {
- }
-
- #endregion
-
- /// <summary>
- /// Sets a flag indicating that this message is received (as opposed to sent).
- /// </summary>
- internal void SetAsIncoming() {
- this.incoming = true;
- }
-
- /// <summary>
- /// Gets some string from a given version of the OpenID protocol.
- /// </summary>
- /// <param name="protocolVersion">The protocol version to use for lookup.</param>
- /// <param name="mode">A function that can retrieve the desired protocol constant.</param>
- /// <returns>The value of the constant.</returns>
- /// <remarks>
- /// This method can be used by a constructor to throw an <see cref="ArgumentNullException"/>
- /// instead of a <see cref="NullReferenceException"/>.
- /// </remarks>
- protected static string GetProtocolConstant(Version protocolVersion, Func<Protocol, string> mode) {
- ErrorUtilities.VerifyArgumentNotNull(protocolVersion, "protocolVersion");
- return mode(Protocol.Lookup(protocolVersion));
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="RequestBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A common base class for OpenID request messages and indirect responses (since they are ultimately requests).
+ /// </summary>
+ [DebuggerDisplay("OpenID {ProtocolVersion} {Mode}")]
+ internal class RequestBase : IDirectedProtocolMessage {
+ /// <summary>
+ /// The openid.ns parameter in the message.
+ /// </summary>
+ /// <value>"http://specs.openid.net/auth/2.0" </value>
+ /// <remarks>
+ /// This particular value MUST be present for the request to be a valid OpenID Authentication 2.0 request. Future versions of the specification may define different values in order to allow message recipients to properly interpret the request.
+ /// </remarks>
+ [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Read by reflection.")]
+ [MessagePart("openid.ns", IsRequired = true, AllowEmpty = false, MinVersion = "2.0")]
+#pragma warning disable 0414 // read by reflection
+ private readonly string OpenIdNamespace = Protocol.OpenId2Namespace;
+#pragma warning restore 0414
+
+ /// <summary>
+ /// Backing store for the <see cref="Incoming"/> property.
+ /// </summary>
+ private bool incoming;
+
+ /// <summary>
+ /// Backing store for the <see cref="ExtraData"/> property.
+ /// </summary>
+ private Dictionary<string, string> extraData = new Dictionary<string, string>();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RequestBase"/> class.
+ /// </summary>
+ /// <param name="version">The OpenID version this message must comply with.</param>
+ /// <param name="providerEndpoint">The OpenID Provider endpoint.</param>
+ /// <param name="mode">The value for the openid.mode parameter.</param>
+ /// <param name="transport">A value indicating whether the message will be transmitted directly or indirectly.</param>
+ protected RequestBase(Version version, Uri providerEndpoint, string mode, MessageTransport transport) {
+ if (providerEndpoint == null) {
+ throw new ArgumentNullException("providerEndpoint");
+ }
+ if (String.IsNullOrEmpty(mode)) {
+ throw new ArgumentNullException("mode");
+ }
+
+ this.Recipient = providerEndpoint;
+ this.Mode = mode;
+ this.Transport = transport;
+ this.Version = version;
+ }
+
+ /// <summary>
+ /// Gets the value of the openid.mode parameter.
+ /// </summary>
+ [MessagePart("openid.mode", IsRequired = true, AllowEmpty = false)]
+ public string Mode { get; private set; }
+
+ #region IDirectedProtocolMessage Members
+
+ /// <summary>
+ /// Gets the preferred method of transport for the message.
+ /// </summary>
+ /// <value>
+ /// For direct messages this is the OpenID mandated POST.
+ /// For indirect messages both GET and POST are allowed.
+ /// </value>
+ HttpDeliveryMethods IDirectedProtocolMessage.HttpMethods {
+ get {
+ HttpDeliveryMethods methods = HttpDeliveryMethods.PostRequest;
+ if (this.Transport == MessageTransport.Indirect) {
+ methods |= HttpDeliveryMethods.GetRequest;
+ }
+ return methods;
+ }
+ }
+
+ /// <summary>
+ /// Gets the recipient of the message.
+ /// </summary>
+ /// <value>The OP endpoint, or the RP return_to.</value>
+ public Uri Recipient {
+ get;
+ private set;
+ }
+
+ #endregion
+
+ #region IProtocolMessage Properties
+
+ /// <summary>
+ /// Gets the version of the protocol this message is prepared to implement.
+ /// </summary>
+ /// <value>Version 2.0</value>
+ public Version Version { get; private set; }
+
+ /// <summary>
+ /// Gets the level of protection this message requires.
+ /// </summary>
+ /// <value><see cref="MessageProtections.None"/></value>
+ public virtual MessageProtections RequiredProtection {
+ get { return MessageProtections.None; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this is a direct or indirect message.
+ /// </summary>
+ /// <value><see cref="MessageTransport.Direct"/></value>
+ public MessageTransport Transport { get; private set; }
+
+ /// <summary>
+ /// Gets the extra parameters included in the message.
+ /// </summary>
+ /// <value>An empty dictionary.</value>
+ public IDictionary<string, string> ExtraData {
+ get { return this.extraData; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this message was deserialized as an incoming message.
+ /// </summary>
+ bool IMessage.Incoming {
+ get { return this.incoming; }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets the protocol used by this message.
+ /// </summary>
+ protected Protocol Protocol {
+ get { return Protocol.Lookup(this.Version); }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this message was deserialized as an incoming message.
+ /// </summary>
+ protected bool Incoming {
+ get { return this.incoming; }
+ }
+
+ #region IProtocolMessage Methods
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ public virtual void EnsureValidMessage() {
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Sets a flag indicating that this message is received (as opposed to sent).
+ /// </summary>
+ internal void SetAsIncoming() {
+ this.incoming = true;
+ }
+
+ /// <summary>
+ /// Gets some string from a given version of the OpenID protocol.
+ /// </summary>
+ /// <param name="protocolVersion">The protocol version to use for lookup.</param>
+ /// <param name="mode">A function that can retrieve the desired protocol constant.</param>
+ /// <returns>The value of the constant.</returns>
+ /// <remarks>
+ /// This method can be used by a constructor to throw an <see cref="ArgumentNullException"/>
+ /// instead of a <see cref="NullReferenceException"/>.
+ /// </remarks>
+ protected static string GetProtocolConstant(Version protocolVersion, Func<Protocol, string> mode) {
+ ErrorUtilities.VerifyArgumentNotNull(protocolVersion, "protocolVersion");
+ return mode(Protocol.Lookup(protocolVersion));
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs
index 8bac9ae..4405204 100644
--- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs
+++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs
@@ -1,279 +1,297 @@
-//------------------------------------------------------------------------------
-// <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.OpenId {
- 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 OpenIdStrings {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal OpenIdStrings() {
- }
-
- /// <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.OpenId.OpenIdStrings", typeof(OpenIdStrings).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 The association and nonce stores must either be both null or both non-null..
- /// </summary>
- internal static string AssociationAndNonceStoresMustBeBothNullOrBothNonNull {
- get {
- return ResourceManager.GetString("AssociationAndNonceStoresMustBeBothNullOrBothNonNull", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The length of the shared secret ({0}) does not match the length required by the association type (&apos;{1}&apos;)..
- /// </summary>
- internal static string AssociationSecretAndTypeLengthMismatch {
- get {
- return ResourceManager.GetString("AssociationSecretAndTypeLengthMismatch", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The length of the encrypted shared secret ({0}) does not match the length of the hashing algorithm ({1})..
- /// </summary>
- internal static string AssociationSecretHashLengthMismatch {
- get {
- return ResourceManager.GetString("AssociationSecretHashLengthMismatch", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The private data supplied does not meet the requirements of any known Association type. Its length may be too short, or it may have been corrupted..
- /// </summary>
- internal static string BadAssociationPrivateData {
- get {
- return ResourceManager.GetString("BadAssociationPrivateData", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The openid.claimed_id and openid.identity parameters must both be present or both be absent..
- /// </summary>
- internal static string ClaimedIdAndLocalIdMustBothPresentOrAbsent {
- get {
- return ResourceManager.GetString("ClaimedIdAndLocalIdMustBothPresentOrAbsent", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The following properties must be set before the Diffie-Hellman algorithm can generate a public key: {0}.
- /// </summary>
- internal static string DiffieHellmanRequiredPropertiesNotSet {
- get {
- return ResourceManager.GetString("DiffieHellmanRequiredPropertiesNotSet", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to URI is not SSL yet requireSslDiscovery is set to true..
- /// </summary>
- internal static string ExplicitHttpUriSuppliedWithSslRequirement {
- get {
- return ResourceManager.GetString("ExplicitHttpUriSuppliedWithSslRequirement", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Cannot encode &apos;{0}&apos; because it contains an illegal character for Key-Value Form encoding. (line {1}: &apos;{2}&apos;).
- /// </summary>
- internal static string InvalidCharacterInKeyValueFormInput {
- get {
- return ResourceManager.GetString("InvalidCharacterInKeyValueFormInput", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Cannot decode Key-Value Form because a line was found without a &apos;{0}&apos; character. (line {1}: &apos;{2}&apos;).
- /// </summary>
- internal static string InvalidKeyValueFormCharacterMissing {
- get {
- return ResourceManager.GetString("InvalidKeyValueFormCharacterMissing", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The scheme must be http or https but was &apos;{0}&apos;..
- /// </summary>
- internal static string InvalidScheme {
- get {
- return ResourceManager.GetString("InvalidScheme", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The value &apos;{0}&apos; is not a valid URI..
- /// </summary>
- internal static string InvalidUri {
- get {
- return ResourceManager.GetString("InvalidUri", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Not a recognized XRI format: &apos;{0}&apos;..
- /// </summary>
- internal static string InvalidXri {
- get {
- return ResourceManager.GetString("InvalidXri", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The list of keys do not match the provided dictionary..
- /// </summary>
- internal static string KeysListAndDictionaryDoNotMatch {
- get {
- return ResourceManager.GetString("KeysListAndDictionaryDoNotMatch", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to No recognized association type matches the requested length of {0}..
- /// </summary>
- internal static string NoAssociationTypeFoundByLength {
- get {
- return ResourceManager.GetString("NoAssociationTypeFoundByLength", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to No recognized association type matches the requested name of &apos;{0}&apos;..
- /// </summary>
- internal static string NoAssociationTypeFoundByName {
- get {
- return ResourceManager.GetString("NoAssociationTypeFoundByName", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Unless using transport layer encryption, &quot;no-encryption&quot; MUST NOT be used..
- /// </summary>
- internal static string NoEncryptionSessionRequiresHttps {
- get {
- return ResourceManager.GetString("NoEncryptionSessionRequiresHttps", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Diffie-Hellman session type &apos;{0}&apos; not found for OpenID {1}..
- /// </summary>
- internal static string NoSessionTypeFound {
- get {
- return ResourceManager.GetString("NoSessionTypeFound", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to Unable to determine the version of the OpenID protocol implemented by the Provider at endpoint &apos;{0}&apos;..
- /// </summary>
- internal static string ProviderVersionUnrecognized {
- get {
- return ResourceManager.GetString("ProviderVersionUnrecognized", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to An HTTP request to the realm URL ({0}) resulted in a redirect, which is not allowed during relying party discovery..
- /// </summary>
- internal static string RealmCausedRedirectUponDiscovery {
- get {
- return ResourceManager.GetString("RealmCausedRedirectUponDiscovery", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to return_to &apos;{0}&apos; not under realm &apos;{1}&apos;..
- /// </summary>
- internal static string ReturnToNotUnderRealm {
- get {
- return ResourceManager.GetString("ReturnToNotUnderRealm", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The openid.return_to parameter is required in the request message in order to construct a response, but that parameter was missing..
- /// </summary>
- internal static string ReturnToRequiredForResponse {
- get {
- return ResourceManager.GetString("ReturnToRequiredForResponse", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to The following parameter(s) are not included in the signature but must be: {0}.
- /// </summary>
- internal static string SignatureDoesNotIncludeMandatoryParts {
- get {
- return ResourceManager.GetString("SignatureDoesNotIncludeMandatoryParts", 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 {
- get {
- return ResourceManager.GetString("UserSetupUrlRequiredInImmediateNegativeResponse", resourceCulture);
- }
- }
-
- /// <summary>
- /// Looks up a localized string similar to XRI resolution failed..
- /// </summary>
- internal static string XriResolutionFailed {
- get {
- return ResourceManager.GetString("XriResolutionFailed", 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.OpenId {
+ 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 OpenIdStrings {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal OpenIdStrings() {
+ }
+
+ /// <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.OpenId.OpenIdStrings", typeof(OpenIdStrings).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 The association and nonce stores must either be both null or both non-null..
+ /// </summary>
+ internal static string AssociationAndNonceStoresMustBeBothNullOrBothNonNull {
+ get {
+ return ResourceManager.GetString("AssociationAndNonceStoresMustBeBothNullOrBothNonNull", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The length of the shared secret ({0}) does not match the length required by the association type (&apos;{1}&apos;)..
+ /// </summary>
+ internal static string AssociationSecretAndTypeLengthMismatch {
+ get {
+ return ResourceManager.GetString("AssociationSecretAndTypeLengthMismatch", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The length of the encrypted shared secret ({0}) does not match the length of the hashing algorithm ({1})..
+ /// </summary>
+ internal static string AssociationSecretHashLengthMismatch {
+ get {
+ return ResourceManager.GetString("AssociationSecretHashLengthMismatch", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The private data supplied does not meet the requirements of any known Association type. Its length may be too short, or it may have been corrupted..
+ /// </summary>
+ internal static string BadAssociationPrivateData {
+ get {
+ return ResourceManager.GetString("BadAssociationPrivateData", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A Simple Registration request can only generate a response on the receiving end..
+ /// </summary>
+ internal static string CallDeserializeBeforeCreateResponse {
+ get {
+ return ResourceManager.GetString("CallDeserializeBeforeCreateResponse", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The openid.claimed_id and openid.identity parameters must both be present or both be absent..
+ /// </summary>
+ internal static string ClaimedIdAndLocalIdMustBothPresentOrAbsent {
+ get {
+ return ResourceManager.GetString("ClaimedIdAndLocalIdMustBothPresentOrAbsent", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The following properties must be set before the Diffie-Hellman algorithm can generate a public key: {0}.
+ /// </summary>
+ internal static string DiffieHellmanRequiredPropertiesNotSet {
+ get {
+ return ResourceManager.GetString("DiffieHellmanRequiredPropertiesNotSet", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to URI is not SSL yet requireSslDiscovery is set to true..
+ /// </summary>
+ internal static string ExplicitHttpUriSuppliedWithSslRequirement {
+ get {
+ return ResourceManager.GetString("ExplicitHttpUriSuppliedWithSslRequirement", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to An extension sharing namespace &apos;{0}&apos; has already been added. Only one extension per namespace is allowed in a given request..
+ /// </summary>
+ internal static string ExtensionAlreadyAddedWithSameTypeURI {
+ get {
+ return ResourceManager.GetString("ExtensionAlreadyAddedWithSameTypeURI", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Cannot encode &apos;{0}&apos; because it contains an illegal character for Key-Value Form encoding. (line {1}: &apos;{2}&apos;).
+ /// </summary>
+ internal static string InvalidCharacterInKeyValueFormInput {
+ get {
+ return ResourceManager.GetString("InvalidCharacterInKeyValueFormInput", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Cannot decode Key-Value Form because a line was found without a &apos;{0}&apos; character. (line {1}: &apos;{2}&apos;).
+ /// </summary>
+ internal static string InvalidKeyValueFormCharacterMissing {
+ get {
+ return ResourceManager.GetString("InvalidKeyValueFormCharacterMissing", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The scheme must be http or https but was &apos;{0}&apos;..
+ /// </summary>
+ internal static string InvalidScheme {
+ get {
+ return ResourceManager.GetString("InvalidScheme", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The value &apos;{0}&apos; is not a valid URI..
+ /// </summary>
+ internal static string InvalidUri {
+ get {
+ return ResourceManager.GetString("InvalidUri", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Not a recognized XRI format: &apos;{0}&apos;..
+ /// </summary>
+ internal static string InvalidXri {
+ get {
+ return ResourceManager.GetString("InvalidXri", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The list of keys do not match the provided dictionary..
+ /// </summary>
+ internal static string KeysListAndDictionaryDoNotMatch {
+ get {
+ return ResourceManager.GetString("KeysListAndDictionaryDoNotMatch", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to No recognized association type matches the requested length of {0}..
+ /// </summary>
+ internal static string NoAssociationTypeFoundByLength {
+ get {
+ return ResourceManager.GetString("NoAssociationTypeFoundByLength", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to No recognized association type matches the requested name of &apos;{0}&apos;..
+ /// </summary>
+ internal static string NoAssociationTypeFoundByName {
+ get {
+ return ResourceManager.GetString("NoAssociationTypeFoundByName", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Unless using transport layer encryption, &quot;no-encryption&quot; MUST NOT be used..
+ /// </summary>
+ internal static string NoEncryptionSessionRequiresHttps {
+ get {
+ return ResourceManager.GetString("NoEncryptionSessionRequiresHttps", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Diffie-Hellman session type &apos;{0}&apos; not found for OpenID {1}..
+ /// </summary>
+ internal static string NoSessionTypeFound {
+ get {
+ return ResourceManager.GetString("NoSessionTypeFound", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Unable to determine the version of the OpenID protocol implemented by the Provider at endpoint &apos;{0}&apos;..
+ /// </summary>
+ internal static string ProviderVersionUnrecognized {
+ get {
+ return ResourceManager.GetString("ProviderVersionUnrecognized", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to An HTTP request to the realm URL ({0}) resulted in a redirect, which is not allowed during relying party discovery..
+ /// </summary>
+ internal static string RealmCausedRedirectUponDiscovery {
+ get {
+ return ResourceManager.GetString("RealmCausedRedirectUponDiscovery", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to return_to &apos;{0}&apos; not under realm &apos;{1}&apos;..
+ /// </summary>
+ internal static string ReturnToNotUnderRealm {
+ get {
+ return ResourceManager.GetString("ReturnToNotUnderRealm", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The openid.return_to parameter is required in the request message in order to construct a response, but that parameter was missing..
+ /// </summary>
+ internal static string ReturnToRequiredForResponse {
+ get {
+ return ResourceManager.GetString("ReturnToRequiredForResponse", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The following parameter(s) are not included in the signature but must be: {0}.
+ /// </summary>
+ internal static string SignatureDoesNotIncludeMandatoryParts {
+ get {
+ return ResourceManager.GetString("SignatureDoesNotIncludeMandatoryParts", 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 {
+ get {
+ return ResourceManager.GetString("UserSetupUrlRequiredInImmediateNegativeResponse", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to XRI resolution failed..
+ /// </summary>
+ internal static string XriResolutionFailed {
+ get {
+ return ResourceManager.GetString("XriResolutionFailed", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx
index 51d52a6..f7f134d 100644
--- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx
+++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx
@@ -1,192 +1,198 @@
-<?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="AssociationAndNonceStoresMustBeBothNullOrBothNonNull" xml:space="preserve">
- <value>The association and nonce stores must either be both null or both non-null.</value>
- </data>
- <data name="AssociationSecretAndTypeLengthMismatch" xml:space="preserve">
- <value>The length of the shared secret ({0}) does not match the length required by the association type ('{1}').</value>
- </data>
- <data name="AssociationSecretHashLengthMismatch" xml:space="preserve">
- <value>The length of the encrypted shared secret ({0}) does not match the length of the hashing algorithm ({1}).</value>
- </data>
- <data name="BadAssociationPrivateData" xml:space="preserve">
- <value>The private data supplied does not meet the requirements of any known Association type. Its length may be too short, or it may have been corrupted.</value>
- </data>
- <data name="ClaimedIdAndLocalIdMustBothPresentOrAbsent" xml:space="preserve">
- <value>The openid.claimed_id and openid.identity parameters must both be present or both be absent.</value>
- </data>
- <data name="DiffieHellmanRequiredPropertiesNotSet" xml:space="preserve">
- <value>The following properties must be set before the Diffie-Hellman algorithm can generate a public key: {0}</value>
- </data>
- <data name="ExplicitHttpUriSuppliedWithSslRequirement" xml:space="preserve">
- <value>URI is not SSL yet requireSslDiscovery is set to true.</value>
- </data>
- <data name="InvalidCharacterInKeyValueFormInput" xml:space="preserve">
- <value>Cannot encode '{0}' because it contains an illegal character for Key-Value Form encoding. (line {1}: '{2}')</value>
- </data>
- <data name="InvalidKeyValueFormCharacterMissing" xml:space="preserve">
- <value>Cannot decode Key-Value Form because a line was found without a '{0}' character. (line {1}: '{2}')</value>
- </data>
- <data name="InvalidScheme" xml:space="preserve">
- <value>The scheme must be http or https but was '{0}'.</value>
- </data>
- <data name="InvalidUri" xml:space="preserve">
- <value>The value '{0}' is not a valid URI.</value>
- </data>
- <data name="InvalidXri" xml:space="preserve">
- <value>Not a recognized XRI format: '{0}'.</value>
- </data>
- <data name="KeysListAndDictionaryDoNotMatch" xml:space="preserve">
- <value>The list of keys do not match the provided dictionary.</value>
- </data>
- <data name="NoAssociationTypeFoundByLength" xml:space="preserve">
- <value>No recognized association type matches the requested length of {0}.</value>
- </data>
- <data name="NoAssociationTypeFoundByName" xml:space="preserve">
- <value>No recognized association type matches the requested name of '{0}'.</value>
- </data>
- <data name="NoEncryptionSessionRequiresHttps" xml:space="preserve">
- <value>Unless using transport layer encryption, "no-encryption" MUST NOT be used.</value>
- </data>
- <data name="NoSessionTypeFound" xml:space="preserve">
- <value>Diffie-Hellman session type '{0}' not found for OpenID {1}.</value>
- </data>
- <data name="ProviderVersionUnrecognized" xml:space="preserve">
- <value>Unable to determine the version of the OpenID protocol implemented by the Provider at endpoint '{0}'.</value>
- </data>
- <data name="RealmCausedRedirectUponDiscovery" xml:space="preserve">
- <value>An HTTP request to the realm URL ({0}) resulted in a redirect, which is not allowed during relying party discovery.</value>
- </data>
- <data name="ReturnToNotUnderRealm" xml:space="preserve">
- <value>return_to '{0}' not under realm '{1}'.</value>
- </data>
- <data name="ReturnToRequiredForResponse" xml:space="preserve">
- <value>The openid.return_to parameter is required in the request message in order to construct a response, but that parameter was missing.</value>
- </data>
- <data name="SignatureDoesNotIncludeMandatoryParts" xml:space="preserve">
- <value>The following parameter(s) are not included in the signature but must be: {0}</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>
- <data name="XriResolutionFailed" xml:space="preserve">
- <value>XRI resolution 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="AssociationAndNonceStoresMustBeBothNullOrBothNonNull" xml:space="preserve">
+ <value>The association and nonce stores must either be both null or both non-null.</value>
+ </data>
+ <data name="AssociationSecretAndTypeLengthMismatch" xml:space="preserve">
+ <value>The length of the shared secret ({0}) does not match the length required by the association type ('{1}').</value>
+ </data>
+ <data name="AssociationSecretHashLengthMismatch" xml:space="preserve">
+ <value>The length of the encrypted shared secret ({0}) does not match the length of the hashing algorithm ({1}).</value>
+ </data>
+ <data name="BadAssociationPrivateData" xml:space="preserve">
+ <value>The private data supplied does not meet the requirements of any known Association type. Its length may be too short, or it may have been corrupted.</value>
+ </data>
+ <data name="CallDeserializeBeforeCreateResponse" xml:space="preserve">
+ <value>A Simple Registration request can only generate a response on the receiving end.</value>
+ </data>
+ <data name="ClaimedIdAndLocalIdMustBothPresentOrAbsent" xml:space="preserve">
+ <value>The openid.claimed_id and openid.identity parameters must both be present or both be absent.</value>
+ </data>
+ <data name="DiffieHellmanRequiredPropertiesNotSet" xml:space="preserve">
+ <value>The following properties must be set before the Diffie-Hellman algorithm can generate a public key: {0}</value>
+ </data>
+ <data name="ExplicitHttpUriSuppliedWithSslRequirement" xml:space="preserve">
+ <value>URI is not SSL yet requireSslDiscovery is set to true.</value>
+ </data>
+ <data name="ExtensionAlreadyAddedWithSameTypeURI" xml:space="preserve">
+ <value>An extension sharing namespace '{0}' has already been added. Only one extension per namespace is allowed in a given request.</value>
+ </data>
+ <data name="InvalidCharacterInKeyValueFormInput" xml:space="preserve">
+ <value>Cannot encode '{0}' because it contains an illegal character for Key-Value Form encoding. (line {1}: '{2}')</value>
+ </data>
+ <data name="InvalidKeyValueFormCharacterMissing" xml:space="preserve">
+ <value>Cannot decode Key-Value Form because a line was found without a '{0}' character. (line {1}: '{2}')</value>
+ </data>
+ <data name="InvalidScheme" xml:space="preserve">
+ <value>The scheme must be http or https but was '{0}'.</value>
+ </data>
+ <data name="InvalidUri" xml:space="preserve">
+ <value>The value '{0}' is not a valid URI.</value>
+ </data>
+ <data name="InvalidXri" xml:space="preserve">
+ <value>Not a recognized XRI format: '{0}'.</value>
+ </data>
+ <data name="KeysListAndDictionaryDoNotMatch" xml:space="preserve">
+ <value>The list of keys do not match the provided dictionary.</value>
+ </data>
+ <data name="NoAssociationTypeFoundByLength" xml:space="preserve">
+ <value>No recognized association type matches the requested length of {0}.</value>
+ </data>
+ <data name="NoAssociationTypeFoundByName" xml:space="preserve">
+ <value>No recognized association type matches the requested name of '{0}'.</value>
+ </data>
+ <data name="NoEncryptionSessionRequiresHttps" xml:space="preserve">
+ <value>Unless using transport layer encryption, "no-encryption" MUST NOT be used.</value>
+ </data>
+ <data name="NoSessionTypeFound" xml:space="preserve">
+ <value>Diffie-Hellman session type '{0}' not found for OpenID {1}.</value>
+ </data>
+ <data name="ProviderVersionUnrecognized" xml:space="preserve">
+ <value>Unable to determine the version of the OpenID protocol implemented by the Provider at endpoint '{0}'.</value>
+ </data>
+ <data name="RealmCausedRedirectUponDiscovery" xml:space="preserve">
+ <value>An HTTP request to the realm URL ({0}) resulted in a redirect, which is not allowed during relying party discovery.</value>
+ </data>
+ <data name="ReturnToNotUnderRealm" xml:space="preserve">
+ <value>return_to '{0}' not under realm '{1}'.</value>
+ </data>
+ <data name="ReturnToRequiredForResponse" xml:space="preserve">
+ <value>The openid.return_to parameter is required in the request message in order to construct a response, but that parameter was missing.</value>
+ </data>
+ <data name="SignatureDoesNotIncludeMandatoryParts" xml:space="preserve">
+ <value>The following parameter(s) are not included in the signature but must be: {0}</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>
+ <data name="XriResolutionFailed" xml:space="preserve">
+ <value>XRI resolution failed.</value>
+ </data>
</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs b/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs
new file mode 100644
index 0000000..82e6e32
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/OpenIdUtilities.cs
@@ -0,0 +1,28 @@
+//-----------------------------------------------------------------------
+// <copyright file="OpenIdUtilities.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A set of utilities especially useful to OpenID.
+ /// </summary>
+ internal static class OpenIdUtilities {
+ /// <summary>
+ /// Gets the OpenID protocol instance for the version in a message.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <returns>The OpenID protocol instance.</returns>
+ internal static Protocol GetProtocol(this IProtocolMessage message) {
+ ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ return Protocol.Lookup(message.Version);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Util.cs b/src/DotNetOpenAuth/Util.cs
index 00ba56f..d72c8f8 100644
--- a/src/DotNetOpenAuth/Util.cs
+++ b/src/DotNetOpenAuth/Util.cs
@@ -1,176 +1,218 @@
-//-----------------------------------------------------------------------
-// <copyright file="Util.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-namespace DotNetOpenAuth {
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Net;
- using System.Reflection;
- using System.Text;
-
- /// <summary>
- /// A grab-bag utility class.
- /// </summary>
- internal static class Util {
- /// <summary>
- /// Gets a human-readable description of the library name and version, including
- /// whether the build is an official or private one.
- /// </summary>
- public static string LibraryVersion {
- get {
- string assemblyFullName = Assembly.GetExecutingAssembly().FullName;
- bool official = assemblyFullName.Contains("PublicKeyToken=2780ccd10d57b246");
-
- // We use InvariantCulture since this is used for logging.
- return string.Format(CultureInfo.InvariantCulture, "{0} ({1})", assemblyFullName, official ? "official" : "private");
- }
- }
-
- /// <summary>
- /// Tests for equality between two objects. Safely handles the case where one or both are null.
- /// </summary>
- /// <typeparam name="T">The type of objects been checked for equality.</typeparam>
- /// <param name="first">The first object.</param>
- /// <param name="second">The second object.</param>
- /// <returns><c>true</c> if the two objects are equal; <c>false</c> otherwise.</returns>
- internal static bool EqualsNullSafe<T>(this T first, T second) where T : class {
- // If one is null and the other is not...
- if (object.ReferenceEquals(first, null) ^ object.ReferenceEquals(second, null)) {
- return false;
- }
-
- // If both are null... (we only check one because we already know both are either null or non-null)
- if (object.ReferenceEquals(first, null)) {
- return true;
- }
-
- // Neither are null. Delegate to the Equals method.
- return first.Equals(second);
- }
-
- /// <summary>
- /// Prepares a dictionary for printing as a string.
- /// </summary>
- /// <typeparam name="K">The type of the key.</typeparam>
- /// <typeparam name="V">The type of the value.</typeparam>
- /// <param name="pairs">The dictionary or sequence of name-value pairs.</param>
- /// <returns>An object whose ToString method will perform the actual work of generating the string.</returns>
- /// <remarks>
- /// The work isn't done until (and if) the
- /// <see cref="Object.ToString"/> method is actually called, which makes it great
- /// for logging complex objects without being in a conditional block.
- /// </remarks>
- internal static object ToStringDeferred<K, V>(this IEnumerable<KeyValuePair<K, V>> pairs) {
- return new DelayedToString<IEnumerable<KeyValuePair<K, V>>>(
- pairs,
- p => {
- var dictionary = pairs as IDictionary<K, V>;
- StringBuilder sb = new StringBuilder(dictionary != null ? dictionary.Count * 40 : 200);
- foreach (var pair in pairs) {
- sb.AppendFormat("\t{0}: {1}{2}", pair.Key, pair.Value, Environment.NewLine);
- }
- return sb.ToString();
- });
- }
-
- /// <summary>
- /// Offers deferred ToString processing for a list of elements, that are assumed
- /// to generate just a single-line string.
- /// </summary>
- /// <typeparam name="T">The type of elements contained in the list.</typeparam>
- /// <param name="list">The list of elements.</param>
- /// <returns>An object whose ToString method will perform the actual work of generating the string.</returns>
- internal static object ToStringDeferred<T>(this IEnumerable<T> list) {
- return ToStringDeferred<T>(list, false);
- }
-
- /// <summary>
- /// Offers deferred ToString processing for a list of elements.
- /// </summary>
- /// <typeparam name="T">The type of elements contained in the list.</typeparam>
- /// <param name="list">The list of elements.</param>
- /// <param name="multiLineElements">if set to <c>true</c>, special formatting will be applied to the output to make it clear where one element ends and the next begins.</param>
- /// <returns>An object whose ToString method will perform the actual work of generating the string.</returns>
- internal static object ToStringDeferred<T>(this IEnumerable<T> list, bool multiLineElements) {
- return new DelayedToString<IEnumerable<T>>(
- list,
- l => {
- StringBuilder sb = new StringBuilder();
- if (multiLineElements) {
- sb.AppendLine("[{");
- foreach (T obj in l) {
- // Prepare the string repersentation of the object
- string objString = obj != null ? obj.ToString() : "<NULL>";
-
- // Indent every line printed
- objString = objString.Replace(Environment.NewLine, Environment.NewLine + "\t");
- sb.Append("\t");
- sb.Append(objString);
-
- if (!objString.EndsWith(Environment.NewLine)) {
- sb.AppendLine();
- }
- sb.AppendLine("}, {");
- }
- if (sb.Length > 2) { // if anything was in the enumeration
- sb.Length -= 2 + Environment.NewLine.Length; // trim off the last ", {\r\n"
- } else {
- sb.Length -= 1; // trim off the opening {
- }
- sb.Append("]");
- return sb.ToString();
- } else {
- sb.Append("{");
- foreach (T obj in l) {
- sb.Append(obj != null ? obj.ToString() : "<NULL>");
- sb.AppendLine(",");
- }
- if (sb.Length > 1) {
- sb.Length -= 1;
- }
- sb.Append("}");
- return sb.ToString();
- }
- });
- }
-
- /// <summary>
- /// Manages an individual deferred ToString call.
- /// </summary>
- /// <typeparam name="T">The type of object to be serialized as a string.</typeparam>
- private class DelayedToString<T> {
- /// <summary>
- /// The object that will be serialized if called upon.
- /// </summary>
- private T obj;
-
- /// <summary>
- /// The method used to serialize <see cref="obj"/> to string form.
- /// </summary>
- private Func<T, string> toString;
-
- /// <summary>
- /// Initializes a new instance of the DelayedToString class.
- /// </summary>
- /// <param name="obj">The object that may be serialized to string form.</param>
- /// <param name="toString">The method that will serialize the object if called upon.</param>
- public DelayedToString(T obj, Func<T, string> toString) {
- this.obj = obj;
- this.toString = toString;
- }
-
- /// <summary>
- /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
- /// </summary>
- /// <returns>
- /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
- /// </returns>
- public override string ToString() {
- return this.toString(this.obj);
- }
- }
- }
-}
+//-----------------------------------------------------------------------
+// <copyright file="Util.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+namespace DotNetOpenAuth {
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Net;
+ using System.Reflection;
+ using System.Text;
+
+ /// <summary>
+ /// A grab-bag utility class.
+ /// </summary>
+ internal static class Util {
+ /// <summary>
+ /// A set of escaping mappings that help secure a string from javscript execution.
+ /// </summary>
+ /// <remarks>
+ /// The characters to escape here are inspired by
+ /// http://code.google.com/p/doctype/wiki/ArticleXSSInJavaScript
+ /// </remarks>
+ private static readonly Dictionary<string, string> javascriptStaticStringEscaping = new Dictionary<string, string> {
+ {"\\", @"\\" }, // this WAS just above the & substitution but we moved it here to prevent double-escaping
+ {"\t", @"\t" },
+ {"\n", @"\n" },
+ {"\r", @"\r" },
+ {"\u0085", @"\u0085" },
+ {"\u2028", @"\u2028" },
+ {"\u2029", @"\u2029" },
+ {"'", @"\x27" },
+ {"\"", @"\x22" },
+ {"&", @"\x26" },
+ {"<", @"\x3c" },
+ {">", @"\x3e" },
+ {"=", @"\x3d" },
+ };
+
+ /// <summary>
+ /// Gets a human-readable description of the library name and version, including
+ /// whether the build is an official or private one.
+ /// </summary>
+ public static string LibraryVersion {
+ get {
+ string assemblyFullName = Assembly.GetExecutingAssembly().FullName;
+ bool official = assemblyFullName.Contains("PublicKeyToken=2780ccd10d57b246");
+
+ // We use InvariantCulture since this is used for logging.
+ return string.Format(CultureInfo.InvariantCulture, "{0} ({1})", assemblyFullName, official ? "official" : "private");
+ }
+ }
+
+ /// <summary>
+ /// Tests for equality between two objects. Safely handles the case where one or both are null.
+ /// </summary>
+ /// <typeparam name="T">The type of objects been checked for equality.</typeparam>
+ /// <param name="first">The first object.</param>
+ /// <param name="second">The second object.</param>
+ /// <returns><c>true</c> if the two objects are equal; <c>false</c> otherwise.</returns>
+ internal static bool EqualsNullSafe<T>(this T first, T second) where T : class {
+ // If one is null and the other is not...
+ if (object.ReferenceEquals(first, null) ^ object.ReferenceEquals(second, null)) {
+ return false;
+ }
+
+ // If both are null... (we only check one because we already know both are either null or non-null)
+ if (object.ReferenceEquals(first, null)) {
+ return true;
+ }
+
+ // Neither are null. Delegate to the Equals method.
+ return first.Equals(second);
+ }
+
+ /// <summary>
+ /// Prepares what SHOULD be simply a string value for safe injection into Javascript
+ /// by using appropriate character escaping.
+ /// </summary>
+ /// <param name="value">The untrusted string value to be escaped to protected against XSS attacks.</param>
+ /// <returns>The escaped string.</returns>
+ internal static string GetSafeJavascriptValue(string value) {
+ if (value == null) return "null";
+ // We use a StringBuilder because we have potentially many replacements to do,
+ // and we don't want to create a new string for every intermediate replacement step.
+ StringBuilder builder = new StringBuilder(value);
+ foreach (var pair in javascriptStaticStringEscaping) {
+ builder.Replace(pair.Key, pair.Value);
+ }
+ builder.Insert(0, '\'');
+ builder.Append('\'');
+ return builder.ToString();
+ }
+
+ /// <summary>
+ /// Prepares a dictionary for printing as a string.
+ /// </summary>
+ /// <typeparam name="K">The type of the key.</typeparam>
+ /// <typeparam name="V">The type of the value.</typeparam>
+ /// <param name="pairs">The dictionary or sequence of name-value pairs.</param>
+ /// <returns>An object whose ToString method will perform the actual work of generating the string.</returns>
+ /// <remarks>
+ /// The work isn't done until (and if) the
+ /// <see cref="Object.ToString"/> method is actually called, which makes it great
+ /// for logging complex objects without being in a conditional block.
+ /// </remarks>
+ internal static object ToStringDeferred<K, V>(this IEnumerable<KeyValuePair<K, V>> pairs) {
+ return new DelayedToString<IEnumerable<KeyValuePair<K, V>>>(
+ pairs,
+ p => {
+ var dictionary = pairs as IDictionary<K, V>;
+ StringBuilder sb = new StringBuilder(dictionary != null ? dictionary.Count * 40 : 200);
+ foreach (var pair in pairs) {
+ sb.AppendFormat("\t{0}: {1}{2}", pair.Key, pair.Value, Environment.NewLine);
+ }
+ return sb.ToString();
+ });
+ }
+
+ /// <summary>
+ /// Offers deferred ToString processing for a list of elements, that are assumed
+ /// to generate just a single-line string.
+ /// </summary>
+ /// <typeparam name="T">The type of elements contained in the list.</typeparam>
+ /// <param name="list">The list of elements.</param>
+ /// <returns>An object whose ToString method will perform the actual work of generating the string.</returns>
+ internal static object ToStringDeferred<T>(this IEnumerable<T> list) {
+ return ToStringDeferred<T>(list, false);
+ }
+
+ /// <summary>
+ /// Offers deferred ToString processing for a list of elements.
+ /// </summary>
+ /// <typeparam name="T">The type of elements contained in the list.</typeparam>
+ /// <param name="list">The list of elements.</param>
+ /// <param name="multiLineElements">if set to <c>true</c>, special formatting will be applied to the output to make it clear where one element ends and the next begins.</param>
+ /// <returns>An object whose ToString method will perform the actual work of generating the string.</returns>
+ internal static object ToStringDeferred<T>(this IEnumerable<T> list, bool multiLineElements) {
+ return new DelayedToString<IEnumerable<T>>(
+ list,
+ l => {
+ StringBuilder sb = new StringBuilder();
+ if (multiLineElements) {
+ sb.AppendLine("[{");
+ foreach (T obj in l) {
+ // Prepare the string repersentation of the object
+ string objString = obj != null ? obj.ToString() : "<NULL>";
+
+ // Indent every line printed
+ objString = objString.Replace(Environment.NewLine, Environment.NewLine + "\t");
+ sb.Append("\t");
+ sb.Append(objString);
+
+ if (!objString.EndsWith(Environment.NewLine)) {
+ sb.AppendLine();
+ }
+ sb.AppendLine("}, {");
+ }
+ if (sb.Length > 2) { // if anything was in the enumeration
+ sb.Length -= 2 + Environment.NewLine.Length; // trim off the last ", {\r\n"
+ } else {
+ sb.Length -= 1; // trim off the opening {
+ }
+ sb.Append("]");
+ return sb.ToString();
+ } else {
+ sb.Append("{");
+ foreach (T obj in l) {
+ sb.Append(obj != null ? obj.ToString() : "<NULL>");
+ sb.AppendLine(",");
+ }
+ if (sb.Length > 1) {
+ sb.Length -= 1;
+ }
+ sb.Append("}");
+ return sb.ToString();
+ }
+ });
+ }
+
+ /// <summary>
+ /// Manages an individual deferred ToString call.
+ /// </summary>
+ /// <typeparam name="T">The type of object to be serialized as a string.</typeparam>
+ private class DelayedToString<T> {
+ /// <summary>
+ /// The object that will be serialized if called upon.
+ /// </summary>
+ private T obj;
+
+ /// <summary>
+ /// The method used to serialize <see cref="obj"/> to string form.
+ /// </summary>
+ private Func<T, string> toString;
+
+ /// <summary>
+ /// Initializes a new instance of the DelayedToString class.
+ /// </summary>
+ /// <param name="obj">The object that may be serialized to string form.</param>
+ /// <param name="toString">The method that will serialize the object if called upon.</param>
+ public DelayedToString(T obj, Func<T, string> toString) {
+ this.obj = obj;
+ this.toString = toString;
+ }
+
+ /// <summary>
+ /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
+ /// </returns>
+ public override string ToString() {
+ return this.toString(this.obj);
+ }
+ }
+ }
+}