diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2008-12-19 16:24:25 -0800 |
---|---|---|
committer | Andrew <andrewarnott@gmail.com> | 2008-12-19 16:24:25 -0800 |
commit | 89c77e698e1e03f157db71da66576ef564c918ae (patch) | |
tree | aa0e5c871b210d8a792d1847cb76ae283e772aab /src | |
parent | 77f5b1550164c409ee6b4e41c372037832d112c3 (diff) | |
download | DotNetOpenAuth-89c77e698e1e03f157db71da66576ef564c918ae.zip DotNetOpenAuth-89c77e698e1e03f157db71da66576ef564c918ae.tar.gz DotNetOpenAuth-89c77e698e1e03f157db71da66576ef564c918ae.tar.bz2 |
Lots of work toward extensions.
Diffstat (limited to 'src')
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&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 & 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<string, string>. - /// </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<TElement> 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<T>"/> into an <see cref="IComparer<T>"/>. - /// </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&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 & 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<string, string>.
+ /// </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<TElement> 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<T>"/> into an <see cref="IComparer<T>"/>.
+ /// </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<string, string> 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<string, string> 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<string, string> 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<string, string> 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 ('{1}').. - /// </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 '{0}' because it contains an illegal character for Key-Value Form encoding. (line {1}: '{2}'). - /// </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 '{0}' character. (line {1}: '{2}'). - /// </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 '{0}'.. - /// </summary> - internal static string InvalidScheme { - get { - return ResourceManager.GetString("InvalidScheme", resourceCulture); - } - } - - /// <summary> - /// Looks up a localized string similar to The value '{0}' 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: '{0}'.. - /// </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 '{0}'.. - /// </summary> - internal static string NoAssociationTypeFoundByName { - get { - return ResourceManager.GetString("NoAssociationTypeFoundByName", resourceCulture); - } - } - - /// <summary> - /// Looks up a localized string similar to Unless using transport layer encryption, "no-encryption" 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 '{0}' 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 '{0}'.. - /// </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 '{0}' not under realm '{1}'.. - /// </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 ('{1}')..
+ /// </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 '{0}' 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 '{0}' because it contains an illegal character for Key-Value Form encoding. (line {1}: '{2}').
+ /// </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 '{0}' character. (line {1}: '{2}').
+ /// </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 '{0}'..
+ /// </summary>
+ internal static string InvalidScheme {
+ get {
+ return ResourceManager.GetString("InvalidScheme", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The value '{0}' 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: '{0}'..
+ /// </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 '{0}'..
+ /// </summary>
+ internal static string NoAssociationTypeFoundByName {
+ get {
+ return ResourceManager.GetString("NoAssociationTypeFoundByName", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Unless using transport layer encryption, "no-encryption" 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 '{0}' 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 '{0}'..
+ /// </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 '{0}' not under realm '{1}'..
+ /// </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);
+ }
+ }
+ }
+}
|