diff options
Diffstat (limited to 'src')
57 files changed, 3283 insertions, 36 deletions
diff --git a/src/YOURLIBNAME.Test/.gitignore b/src/DotNetOAuth.Test/.gitignore index 58b701f..58b701f 100644 --- a/src/YOURLIBNAME.Test/.gitignore +++ b/src/DotNetOAuth.Test/.gitignore diff --git a/src/YOURLIBNAME.Test/YOURLIBNAME.Test.csproj b/src/DotNetOAuth.Test/DotNetOAuth.Test.csproj index 1dd166f..8f28791 100644 --- a/src/YOURLIBNAME.Test/YOURLIBNAME.Test.csproj +++ b/src/DotNetOAuth.Test/DotNetOAuth.Test.csproj @@ -7,8 +7,8 @@ <ProjectGuid>{4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>YOURLIBNAME.Test</RootNamespace>
- <AssemblyName>YOURLIBNAME.Test</AssemblyName>
+ <RootNamespace>DotNetOAuth.Test</RootNamespace>
+ <AssemblyName>DotNetOAuth.Test</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
@@ -58,18 +58,30 @@ </Reference>
</ItemGroup>
<ItemGroup>
+ <Compile Include="MessagingUtilitiesTest.cs" />
+ <Compile Include="Messaging\ChannelTests.cs" />
+ <Compile Include="Messaging\DictionaryXmlReaderTests.cs" />
+ <Compile Include="Mocks\TestDirectedMessage.cs" />
+ <Compile Include="Mocks\TestBadChannel.cs" />
+ <Compile Include="OAuthChannelTests.cs" />
+ <Compile Include="Messaging\MessageSerializerTests.cs" />
+ <Compile Include="Mocks\TestChannel.cs" />
+ <Compile Include="Mocks\TestMessage.cs" />
+ <Compile Include="Mocks\TestMessageTypeProvider.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="Messaging\ResponseTest.cs" />
+ <Compile Include="ServiceProviderTest.cs" />
<Compile Include="TestBase.cs" />
</ItemGroup>
<ItemGroup>
- <ProjectReference Include="..\YOURLIBNAME\YOURLIBNAME.csproj">
+ <ProjectReference Include="..\DotNetOAuth\DotNetOAuth.csproj">
<Project>{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}</Project>
- <Name>YOURLIBNAME</Name>
+ <Name>DotNetOAuth</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Logging.config" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
- <Import Project="..\..\tools\YOURLIBNAME.Versioning.targets" />
+ <Import Project="..\..\tools\DotNetOAuth.Versioning.targets" />
</Project>
\ No newline at end of file diff --git a/src/YOURLIBNAME.Test/Logging.config b/src/DotNetOAuth.Test/Logging.config index a1d675b..a1d675b 100644 --- a/src/YOURLIBNAME.Test/Logging.config +++ b/src/DotNetOAuth.Test/Logging.config diff --git a/src/DotNetOAuth.Test/Messaging/ChannelTests.cs b/src/DotNetOAuth.Test/Messaging/ChannelTests.cs new file mode 100644 index 0000000..a2909c4 --- /dev/null +++ b/src/DotNetOAuth.Test/Messaging/ChannelTests.cs @@ -0,0 +1,107 @@ +//-----------------------------------------------------------------------
+// <copyright file="ChannelTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Test.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Net;
+ using DotNetOAuth.Messaging;
+ using DotNetOAuth.Test.Mocks;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class ChannelTests : TestBase {
+ private Channel channel;
+
+ [TestInitialize]
+ public override void SetUp() {
+ base.SetUp();
+
+ this.channel = new TestChannel();
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void CtorNull() {
+ // This bad channel is deliberately constructed to pass null to
+ // its protected base class' constructor.
+ new TestBadChannel();
+ }
+
+ [TestMethod]
+ public void DequeueIndirectOrResponseMessageReturnsNull() {
+ Assert.IsNull(this.channel.DequeueIndirectOrResponseMessage());
+ }
+
+ [TestMethod]
+ public void ReadFromRequestQueryString() {
+ this.ParameterizedReceiveTest("GET");
+ }
+
+ [TestMethod]
+ public void ReadFromRequestForm() {
+ this.ParameterizedReceiveTest("POST");
+ }
+
+ [TestMethod]
+ public void SendIndirectMessage() {
+ IProtocolMessage message = new TestDirectedMessage {
+ Age = 15,
+ Name = "Andrew",
+ Location = new Uri("http://host/path"),
+ Recipient = new Uri("http://provider/path"),
+ };
+ this.channel.Send(message);
+ Response response = this.channel.DequeueIndirectOrResponseMessage();
+ Assert.AreEqual(HttpStatusCode.Redirect, response.Status);
+ StringAssert.StartsWith(response.Headers[HttpResponseHeader.Location], "http://provider/path");
+ StringAssert.Contains(response.Headers[HttpResponseHeader.Location], "age=15");
+ StringAssert.Contains(response.Headers[HttpResponseHeader.Location], "Name=Andrew");
+ StringAssert.Contains(response.Headers[HttpResponseHeader.Location], "Location=http%3a%2f%2fhost%2fpath");
+ }
+
+ private static HttpRequestInfo CreateHttpRequest(string method, IDictionary<string, string> fields) {
+ string query = MessagingUtilities.CreateQueryString(fields);
+ UriBuilder requestUri = new UriBuilder("http://localhost/path");
+ WebHeaderCollection headers = new WebHeaderCollection();
+ MemoryStream ms = new MemoryStream();
+ if (method == "POST") {
+ headers.Add(HttpRequestHeader.ContentType, "application/x-www-form-urlencoded");
+ StreamWriter sw = new StreamWriter(ms);
+ sw.Write(query);
+ sw.Flush();
+ ms.Position = 0;
+ } else if (method == "GET") {
+ requestUri.Query = query;
+ } else {
+ throw new ArgumentOutOfRangeException("method", method, "Expected POST or GET");
+ }
+ HttpRequestInfo request = new HttpRequestInfo {
+ HttpMethod = method,
+ Url = requestUri.Uri,
+ Headers = headers,
+ InputStream = ms,
+ };
+
+ return request;
+ }
+
+ private void ParameterizedReceiveTest(string method) {
+ var fields = new Dictionary<string, string> {
+ { "age", "15" },
+ { "Name", "Andrew" },
+ { "Location", "http://hostb/pathB" },
+ };
+ IProtocolMessage requestMessage = this.channel.ReadFromRequest(CreateHttpRequest(method, fields));
+ Assert.IsNotNull(requestMessage);
+ Assert.IsInstanceOfType(requestMessage, typeof(TestMessage));
+ TestMessage testMessage = (TestMessage)requestMessage;
+ Assert.AreEqual(15, testMessage.Age);
+ Assert.AreEqual("Andrew", testMessage.Name);
+ Assert.AreEqual("http://hostb/pathB", testMessage.Location.AbsoluteUri);
+ }
+ }
+}
diff --git a/src/DotNetOAuth.Test/Messaging/DictionaryXmlReaderTests.cs b/src/DotNetOAuth.Test/Messaging/DictionaryXmlReaderTests.cs new file mode 100644 index 0000000..3dc3238 --- /dev/null +++ b/src/DotNetOAuth.Test/Messaging/DictionaryXmlReaderTests.cs @@ -0,0 +1,26 @@ +//-----------------------------------------------------------------------
+// <copyright file="DictionaryXmlReaderTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Test.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Xml.Linq;
+ using DotNetOAuth.Messaging;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class DictionaryXmlReaderTests : TestBase {
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void CreateWithNullRootElement() {
+ DictionaryXmlReader.Create(null, new Dictionary<string, string>());
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void CreateWithNullFields() {
+ DictionaryXmlReader.Create(XName.Get("name", "ns"), null);
+ }
+ }
+}
diff --git a/src/DotNetOAuth.Test/Messaging/MessageSerializerTests.cs b/src/DotNetOAuth.Test/Messaging/MessageSerializerTests.cs new file mode 100644 index 0000000..5cb5375 --- /dev/null +++ b/src/DotNetOAuth.Test/Messaging/MessageSerializerTests.cs @@ -0,0 +1,142 @@ +//-----------------------------------------------------------------------
+// <copyright file="MessageSerializerTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Test.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using DotNetOAuth.Messaging;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ /// <summary>
+ /// Tests for the <see cref="MessageSerializer"/> class.
+ /// </summary>
+ [TestClass()]
+ public class MessageSerializerTests : TestBase {
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void SerializeNull() {
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
+ serializer.Serialize(null);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void SerializeNullFields() {
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
+ serializer.Serialize(null, new Mocks.TestMessage());
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void SerializeNullMessage() {
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
+ serializer.Serialize(new Dictionary<string, string>(), null);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void GetInvalidMessageType() {
+ MessageSerializer.Get(typeof(string));
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void GetNullType() {
+ MessageSerializer.Get(null);
+ }
+
+ [TestMethod]
+ public void GetReturnsSameSerializerTwice() {
+ Assert.AreSame(MessageSerializer.Get(typeof(Mocks.TestMessage)), MessageSerializer.Get(typeof(Mocks.TestMessage)));
+ }
+
+ [TestMethod, ExpectedException(typeof(ProtocolException))]
+ public void SerializeInvalidMessage() {
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
+ Dictionary<string, string> fields = new Dictionary<string, string>(StringComparer.Ordinal);
+ Mocks.TestMessage message = new Mocks.TestMessage();
+ message.EmptyMember = "invalidvalue";
+ serializer.Serialize(message);
+ }
+
+ [TestMethod()]
+ public void SerializeTest() {
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
+ var message = new Mocks.TestMessage { Age = 15, Name = "Andrew", Location = new Uri("http://localhost") };
+ IDictionary<string, string> actual = serializer.Serialize(message);
+ Assert.AreEqual(3, actual.Count);
+
+ // Test case sensitivity of generated dictionary
+ Assert.IsFalse(actual.ContainsKey("Age"));
+ Assert.IsTrue(actual.ContainsKey("age"));
+
+ // Test contents of dictionary
+ Assert.AreEqual("15", actual["age"]);
+ Assert.AreEqual("Andrew", actual["Name"]);
+ Assert.AreEqual("http://localhost/", actual["Location"]);
+ Assert.IsFalse(actual.ContainsKey("EmptyMember"));
+ }
+
+ [TestMethod]
+ public void SerializeToExistingDictionary() {
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
+ var message = new Mocks.TestMessage { Age = 15, Name = "Andrew" };
+ var fields = new Dictionary<string, string>();
+ fields["someExtraField"] = "someValue";
+ serializer.Serialize(fields, message);
+ Assert.AreEqual(3, fields.Count);
+ Assert.AreEqual("15", fields["age"]);
+ Assert.AreEqual("Andrew", fields["Name"]);
+ Assert.AreEqual("someValue", fields["someExtraField"]);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void DeserializeNull() {
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
+ serializer.Deserialize(null);
+ }
+
+ [TestMethod()]
+ public void DeserializeSimple() {
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
+ Dictionary<string, string> fields = new Dictionary<string, string>(StringComparer.Ordinal);
+ // We deliberately do this OUT of alphabetical order (caps would go first),
+ // since DataContractSerializer demands things to be IN alphabetical order.
+ fields["age"] = "15";
+ fields["Name"] = "Andrew";
+ var actual = (Mocks.TestMessage)serializer.Deserialize(fields);
+ Assert.AreEqual(15, actual.Age);
+ Assert.AreEqual("Andrew", actual.Name);
+ Assert.IsNull(actual.EmptyMember);
+ }
+
+ [TestMethod]
+ public void DeserializeWithExtraFields() {
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
+ Dictionary<string, string> fields = new Dictionary<string, string>(StringComparer.Ordinal);
+ fields["age"] = "15";
+ fields["Name"] = "Andrew";
+ // Add some field that is not recognized by the class. This simulates a querystring with
+ // more parameters than are actually interesting to the protocol message.
+ fields["someExtraField"] = "asdf";
+ var actual = (Mocks.TestMessage)serializer.Deserialize(fields);
+ Assert.AreEqual(15, actual.Age);
+ Assert.AreEqual("Andrew", actual.Name);
+ Assert.IsNull(actual.EmptyMember);
+ }
+
+ [TestMethod, ExpectedException(typeof(ProtocolException))]
+ public void DeserializeEmpty() {
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
+ var fields = new Dictionary<string, string>(StringComparer.Ordinal);
+ serializer.Deserialize(fields);
+ }
+
+ [TestMethod, ExpectedException(typeof(ProtocolException))]
+ public void DeserializeInvalidMessage() {
+ var serializer = MessageSerializer.Get(typeof(Mocks.TestMessage));
+ Dictionary<string, string> fields = new Dictionary<string, string>(StringComparer.Ordinal);
+ // Set an disallowed value.
+ fields["age"] = "-1";
+ serializer.Deserialize(fields);
+ }
+ }
+}
diff --git a/src/DotNetOAuth.Test/Messaging/ResponseTest.cs b/src/DotNetOAuth.Test/Messaging/ResponseTest.cs new file mode 100644 index 0000000..c1c7dff --- /dev/null +++ b/src/DotNetOAuth.Test/Messaging/ResponseTest.cs @@ -0,0 +1,19 @@ +//-----------------------------------------------------------------------
+// <copyright file="ResponseTest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Test.Messaging {
+ using System;
+ using DotNetOAuth.Messaging;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class ResponseTest : TestBase {
+ [TestMethod, ExpectedException(typeof(InvalidOperationException))]
+ public void SendWithoutAspNetContext() {
+ new Response().Send();
+ }
+ }
+}
diff --git a/src/DotNetOAuth.Test/MessagingUtilitiesTest.cs b/src/DotNetOAuth.Test/MessagingUtilitiesTest.cs new file mode 100644 index 0000000..cc3e552 --- /dev/null +++ b/src/DotNetOAuth.Test/MessagingUtilitiesTest.cs @@ -0,0 +1,99 @@ +//-----------------------------------------------------------------------
+// <copyright file="MessagingUtilitiesTest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Test
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+ using System.IO;
+ using System.Net;
+ using System.Web;
+ using DotNetOAuth.Messaging;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class MessagingUtilitiesTest : TestBase {
+ [TestMethod]
+ public void CreateQueryString() {
+ var args = new Dictionary<string, string>();
+ args.Add("a", "b");
+ args.Add("c/d", "e/f");
+ Assert.AreEqual("a=b&c%2fd=e%2ff", MessagingUtilities.CreateQueryString(args));
+ }
+
+ [TestMethod]
+ public void CreateQueryStringEmptyCollection() {
+ Assert.AreEqual(0, MessagingUtilities.CreateQueryString(new Dictionary<string, string>()).Length);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void CreateQueryStringNullDictionary() {
+ MessagingUtilities.CreateQueryString(null);
+ }
+
+ [TestMethod]
+ public void AppendQueryArgs() {
+ UriBuilder uri = new UriBuilder("http://baseline.org/page");
+ var args = new Dictionary<string, string>();
+ args.Add("a", "b");
+ args.Add("c/d", "e/f");
+ MessagingUtilities.AppendQueryArgs(uri, args);
+ Assert.AreEqual("http://baseline.org/page?a=b&c%2fd=e%2ff", uri.Uri.AbsoluteUri);
+ args.Clear();
+ args.Add("g", "h");
+ MessagingUtilities.AppendQueryArgs(uri, args);
+ Assert.AreEqual("http://baseline.org/page?a=b&c%2fd=e%2ff&g=h", uri.Uri.AbsoluteUri);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void AppendQueryArgsNullUriBuilder() {
+ MessagingUtilities.AppendQueryArgs(null, new Dictionary<string, string>());
+ }
+
+ [TestMethod]
+ public void AppendQueryArgsNullDictionary() {
+ MessagingUtilities.AppendQueryArgs(new UriBuilder(), null);
+ }
+
+ [TestMethod]
+ public void ToDictionary() {
+ NameValueCollection nvc = new NameValueCollection();
+ nvc["a"] = "b";
+ nvc["c"] = "d";
+ Dictionary<string, string> actual = MessagingUtilities.ToDictionary(nvc);
+ Assert.AreEqual(nvc.Count, actual.Count);
+ Assert.AreEqual(nvc["a"], actual["a"]);
+ Assert.AreEqual(nvc["c"], actual["c"]);
+ }
+
+ [TestMethod]
+ public void ToDictionaryNull() {
+ Assert.IsNull(MessagingUtilities.ToDictionary(null));
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void ApplyHeadersToResponseNullResponse() {
+ MessagingUtilities.ApplyHeadersToResponse(new WebHeaderCollection(), null);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void ApplyHeadersToResponseNullHeaders() {
+ MessagingUtilities.ApplyHeadersToResponse(null, new HttpResponse(new StringWriter()));
+ }
+
+ [TestMethod]
+ public void ApplyHeadersToResponse() {
+ var headers = new WebHeaderCollection();
+ headers[HttpResponseHeader.ContentType] = "application/binary";
+
+ var response = new HttpResponse(new StringWriter());
+ MessagingUtilities.ApplyHeadersToResponse(headers, response);
+
+ Assert.AreEqual(headers[HttpResponseHeader.ContentType], response.ContentType);
+ }
+ }
+}
diff --git a/src/DotNetOAuth.Test/Mocks/TestBadChannel.cs b/src/DotNetOAuth.Test/Mocks/TestBadChannel.cs new file mode 100644 index 0000000..20fe03a --- /dev/null +++ b/src/DotNetOAuth.Test/Mocks/TestBadChannel.cs @@ -0,0 +1,42 @@ +//-----------------------------------------------------------------------
+// <copyright file="TestBadChannel.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Test.Mocks {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOAuth.Messaging;
+
+ /// <summary>
+ /// A Channel derived type that passes null to the protected constructor.
+ /// </summary>
+ internal class TestBadChannel : Channel {
+ internal TestBadChannel()
+ : base(null) {
+ }
+
+ protected internal override IProtocolMessage Request(IDirectedProtocolMessage request) {
+ throw new NotImplementedException();
+ }
+
+ protected internal override IProtocolMessage ReadFromResponse(System.IO.Stream responseStream) {
+ throw new NotImplementedException();
+ }
+
+ protected override void SendDirectMessageResponse(IProtocolMessage response) {
+ throw new NotImplementedException();
+ }
+
+ protected override void ReportErrorToUser(ProtocolException exception) {
+ throw new NotImplementedException();
+ }
+
+ protected override void ReportErrorAsDirectResponse(ProtocolException exception) {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/DotNetOAuth.Test/Mocks/TestChannel.cs b/src/DotNetOAuth.Test/Mocks/TestChannel.cs new file mode 100644 index 0000000..1920a38 --- /dev/null +++ b/src/DotNetOAuth.Test/Mocks/TestChannel.cs @@ -0,0 +1,39 @@ +//-----------------------------------------------------------------------
+// <copyright file="TestChannel.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Test.Mocks {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOAuth.Messaging;
+
+ internal class TestChannel : Channel {
+ internal TestChannel()
+ : base(new TestMessageTypeProvider()) {
+ }
+
+ protected internal override IProtocolMessage Request(IDirectedProtocolMessage request) {
+ throw new NotImplementedException();
+ }
+
+ protected internal override IProtocolMessage ReadFromResponse(System.IO.Stream responseStream) {
+ throw new NotImplementedException();
+ }
+
+ protected override void SendDirectMessageResponse(IProtocolMessage response) {
+ throw new NotImplementedException();
+ }
+
+ protected override void ReportErrorToUser(ProtocolException exception) {
+ throw new NotImplementedException();
+ }
+
+ protected override void ReportErrorAsDirectResponse(ProtocolException exception) {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/DotNetOAuth.Test/Mocks/TestDirectedMessage.cs b/src/DotNetOAuth.Test/Mocks/TestDirectedMessage.cs new file mode 100644 index 0000000..6972624 --- /dev/null +++ b/src/DotNetOAuth.Test/Mocks/TestDirectedMessage.cs @@ -0,0 +1,47 @@ +//-----------------------------------------------------------------------
+// <copyright file="TestDirectedMessage.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Test.Mocks {
+ using System;
+ using System.Runtime.Serialization;
+ using DotNetOAuth.Messaging;
+
+ [DataContract(Namespace = Protocol.DataContractNamespaceV10)]
+ internal class TestDirectedMessage : IDirectedProtocolMessage {
+ [DataMember(Name = "age", IsRequired = true)]
+ public int Age { get; set; }
+ [DataMember]
+ public string Name { get; set; }
+ [DataMember]
+ public string EmptyMember { get; set; }
+ [DataMember]
+ public Uri Location { get; set; }
+
+ #region IDirectedProtocolMessage Members
+
+ public Uri Recipient { get; internal set; }
+
+ #endregion
+
+ #region IProtocolMessage Members
+
+ Protocol IProtocolMessage.Protocol {
+ get { return Protocol.V10; }
+ }
+
+ MessageTransport IProtocolMessage.Transport {
+ get { return MessageTransport.Direct; }
+ }
+
+ void IProtocolMessage.EnsureValidMessage() {
+ if (this.EmptyMember != null || this.Age < 0) {
+ throw new ProtocolException();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOAuth.Test/Mocks/TestMessage.cs b/src/DotNetOAuth.Test/Mocks/TestMessage.cs new file mode 100644 index 0000000..8c47717 --- /dev/null +++ b/src/DotNetOAuth.Test/Mocks/TestMessage.cs @@ -0,0 +1,41 @@ +//-----------------------------------------------------------------------
+// <copyright file="TestMessage.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Test.Mocks {
+ using System;
+ using System.Runtime.Serialization;
+ using DotNetOAuth.Messaging;
+
+ [DataContract(Namespace = Protocol.DataContractNamespaceV10)]
+ internal class TestMessage : IProtocolMessage {
+ [DataMember(Name = "age", IsRequired = true)]
+ public int Age { get; set; }
+ [DataMember]
+ public string Name { get; set; }
+ [DataMember]
+ public string EmptyMember { get; set; }
+ [DataMember]
+ public Uri Location { get; set; }
+
+ #region IProtocolMessage Members
+
+ Protocol IProtocolMessage.Protocol {
+ get { return Protocol.V10; }
+ }
+
+ MessageTransport IProtocolMessage.Transport {
+ get { return MessageTransport.Direct; }
+ }
+
+ void IProtocolMessage.EnsureValidMessage() {
+ if (this.EmptyMember != null || this.Age < 0) {
+ throw new ProtocolException();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOAuth.Test/Mocks/TestMessageTypeProvider.cs b/src/DotNetOAuth.Test/Mocks/TestMessageTypeProvider.cs new file mode 100644 index 0000000..647a09f --- /dev/null +++ b/src/DotNetOAuth.Test/Mocks/TestMessageTypeProvider.cs @@ -0,0 +1,31 @@ +//-----------------------------------------------------------------------
+// <copyright file="TestMessageTypeProvider.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Test.Mocks {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOAuth.Messaging;
+
+ internal class TestMessageTypeProvider : IMessageTypeProvider {
+ #region IMessageTypeProvider Members
+
+ public Type GetRequestMessageType(IDictionary<string, string> fields) {
+ if (fields.ContainsKey("age")) {
+ return typeof(TestMessage);
+ } else {
+ return null;
+ }
+ }
+
+ public Type GetResponseMessageType(IProtocolMessage request, IDictionary<string, string> fields) {
+ return this.GetRequestMessageType(fields);
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOAuth.Test/OAuthChannelTests.cs b/src/DotNetOAuth.Test/OAuthChannelTests.cs new file mode 100644 index 0000000..1099dc6 --- /dev/null +++ b/src/DotNetOAuth.Test/OAuthChannelTests.cs @@ -0,0 +1,49 @@ +//-----------------------------------------------------------------------
+// <copyright file="OAuthChannelTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Test {
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+ using DotNetOAuth.Messaging;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class OAuthChannelTests : TestBase {
+ private Channel channel;
+
+ [TestInitialize]
+ public override void SetUp() {
+ base.SetUp();
+
+ this.channel = new OAuthChannel();
+ }
+
+ [TestMethod, Ignore]
+ public void ReadFromRequestAuthorization() {
+ }
+
+ internal static string CreateAuthorizationHeader(IDictionary<string, string> fields) {
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+
+ StringBuilder authorization = new StringBuilder();
+ authorization.Append("OAuth ");
+ foreach (var pair in fields) {
+ string key = Uri.EscapeDataString(pair.Key);
+ string value = Uri.EscapeDataString(pair.Value);
+ authorization.Append(key);
+ authorization.Append("=\"");
+ authorization.Append(value);
+ authorization.Append("\",");
+ }
+ authorization.Length--; // remove trailing comma
+
+ return authorization.ToString();
+ }
+ }
+}
diff --git a/src/YOURLIBNAME.Test/Properties/AssemblyInfo.cs b/src/DotNetOAuth.Test/Properties/AssemblyInfo.cs index c37288a..2495cc7 100644 --- a/src/YOURLIBNAME.Test/Properties/AssemblyInfo.cs +++ b/src/DotNetOAuth.Test/Properties/AssemblyInfo.cs @@ -3,6 +3,7 @@ // Copyright (c) Andrew Arnott. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
+
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -10,11 +11,11 @@ using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
-[assembly: AssemblyTitle("YOURLIBNAME.Test")]
+[assembly: AssemblyTitle("DotNetOAuth.Test")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("YOURLIBNAME.Test")]
+[assembly: AssemblyProduct("DotNetOAuth.Test")]
[assembly: AssemblyCopyright("Copyright © 2008")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
diff --git a/src/DotNetOAuth.Test/ServiceProviderTest.cs b/src/DotNetOAuth.Test/ServiceProviderTest.cs new file mode 100644 index 0000000..d4bced7 --- /dev/null +++ b/src/DotNetOAuth.Test/ServiceProviderTest.cs @@ -0,0 +1,65 @@ +//-----------------------------------------------------------------------
+// <copyright file="ServiceProviderTest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Test {
+ using System;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ /// <summary>
+ /// Tests for the <see cref="ServiceProvider"/> class.
+ /// </summary>
+ [TestClass]
+ public class ServiceProviderTest : TestBase {
+ /// <summary>
+ /// A test for UserAuthorizationUri
+ /// </summary>
+ [TestMethod]
+ public void UserAuthorizationUriTest() {
+ ServiceProvider target = new ServiceProvider();
+ Uri expected = new Uri("http://localhost/authorization");
+ Uri actual;
+ target.UserAuthorizationUri = expected;
+ actual = target.UserAuthorizationUri;
+ Assert.AreEqual(expected, actual);
+ }
+
+ /// <summary>
+ /// A test for RequestTokenUri
+ /// </summary>
+ [TestMethod()]
+ public void RequestTokenUriTest() {
+ ServiceProvider target = new ServiceProvider();
+ Uri expected = new Uri("http://localhost/requesttoken");
+ Uri actual;
+ target.RequestTokenUri = expected;
+ actual = target.RequestTokenUri;
+ Assert.AreEqual(expected, actual);
+ }
+
+ /// <summary>
+ /// Verifies that oauth parameters are not allowed in <see cref="ServiceProvider.RequestTokenUri"/>,
+ /// per section OAuth 1.0 section 4.1.
+ /// </summary>
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void RequestTokenUriWithOAuthParametersTest() {
+ ServiceProvider target = new ServiceProvider();
+ target.RequestTokenUri = new Uri("http://localhost/requesttoken?oauth_token=something");
+ }
+
+ /// <summary>
+ /// A test for AccessTokenUri
+ /// </summary>
+ [TestMethod()]
+ public void AccessTokenUriTest() {
+ ServiceProvider target = new ServiceProvider();
+ Uri expected = new Uri("http://localhost/accesstoken");
+ Uri actual;
+ target.AccessTokenUri = expected;
+ actual = target.AccessTokenUri;
+ Assert.AreEqual(expected, actual);
+ }
+ }
+}
diff --git a/src/DotNetOAuth.Test/Settings.StyleCop b/src/DotNetOAuth.Test/Settings.StyleCop new file mode 100644 index 0000000..100fae5 --- /dev/null +++ b/src/DotNetOAuth.Test/Settings.StyleCop @@ -0,0 +1,24 @@ +<StyleCopSettings Version="4.3">
+ <Analyzers>
+ <Analyzer AnalyzerId="Microsoft.StyleCop.CSharp.DocumentationRules">
+ <Rules>
+ <Rule Name="ElementsMustBeDocumented">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="Microsoft.StyleCop.CSharp.LayoutRules">
+ <Rules>
+ <Rule Name="SingleLineCommentMustBePrecededByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ </Analyzers>
+</StyleCopSettings>
\ No newline at end of file diff --git a/src/YOURLIBNAME.Test/TestBase.cs b/src/DotNetOAuth.Test/TestBase.cs index 5a9c94f..e41b01c 100644 --- a/src/YOURLIBNAME.Test/TestBase.cs +++ b/src/DotNetOAuth.Test/TestBase.cs @@ -3,7 +3,7 @@ // Copyright (c) Andrew Arnott. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
-namespace YOURLIBNAME.Test {
+namespace DotNetOAuth.Test {
using System.Reflection;
using log4net;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -15,7 +15,7 @@ namespace YOURLIBNAME.Test { /// <summary>
/// The logger that tests should use.
/// </summary>
- internal static readonly ILog Logger = LogManager.GetLogger("YOURLIBNAME.Test");
+ internal static readonly ILog Logger = LogManager.GetLogger("DotNetOAuth.Test");
/// <summary>
/// Gets or sets the test context which provides
@@ -28,7 +28,7 @@ namespace YOURLIBNAME.Test { /// </summary>
[TestInitialize]
public virtual void SetUp() {
- log4net.Config.XmlConfigurator.Configure(Assembly.GetExecutingAssembly().GetManifestResourceStream("YOURLIBNAME.Test.Logging.config"));
+ log4net.Config.XmlConfigurator.Configure(Assembly.GetExecutingAssembly().GetManifestResourceStream("DotNetOAuth.Test.Logging.config"));
}
/// <summary>
diff --git a/src/YOURLIBNAME.sln b/src/DotNetOAuth.sln index 007090f..d4e2360 100644 --- a/src/YOURLIBNAME.sln +++ b/src/DotNetOAuth.sln @@ -1,19 +1,19 @@
Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YOURLIBNAME", "YOURLIBNAME\YOURLIBNAME.csproj", "{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOAuth", "DotNetOAuth\DotNetOAuth.csproj", "{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YOURLIBNAME.Test", "YOURLIBNAME.Test\YOURLIBNAME.Test.csproj", "{4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOAuth.Test", "DotNetOAuth.Test\DotNetOAuth.Test.csproj", "{4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{20B5E173-C3C4-49F8-BD25-E69044075B4D}"
ProjectSection(SolutionItems) = preProject
- YOURLIBNAME.vsmdi = YOURLIBNAME.vsmdi
+ DotNetOAuth.vsmdi = DotNetOAuth.vsmdi
LocalTestRun.testrunconfig = LocalTestRun.testrunconfig
EndProjectSection
EndProject
Global
GlobalSection(TestCaseManagementSettings) = postSolution
- CategoryFile = YOURLIBNAME.vsmdi
+ CategoryFile = DotNetOAuth.vsmdi
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
diff --git a/src/YOURLIBNAME.vsmdi b/src/DotNetOAuth.vsmdi index 027090d..027090d 100644 --- a/src/YOURLIBNAME.vsmdi +++ b/src/DotNetOAuth.vsmdi diff --git a/src/YOURLIBNAME/.gitignore b/src/DotNetOAuth/.gitignore index 2a08796..31c006d 100644 --- a/src/YOURLIBNAME/.gitignore +++ b/src/DotNetOAuth/.gitignore @@ -1,3 +1,4 @@ +StyleCop.Cache *.user bin obj diff --git a/src/DotNetOAuth/ClassDiagram.cd b/src/DotNetOAuth/ClassDiagram.cd new file mode 100644 index 0000000..1219a22 --- /dev/null +++ b/src/DotNetOAuth/ClassDiagram.cd @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8"?>
+<ClassDiagram MajorVersion="1" MinorVersion="1">
+ <Class Name="DotNetOAuth.ServiceProvider">
+ <Position X="0.5" Y="0.5" Width="2" />
+ <TypeIdentifier>
+ <HashCode>AAAAAAAAAAAAAAAAACAAAACAAAAAAAAAAAAABAAAAAQ=</HashCode>
+ <FileName>ServiceProvider.cs</FileName>
+ </TypeIdentifier>
+ </Class>
+ <Class Name="DotNetOAuth.Consumer">
+ <Position X="2.75" Y="0.5" Width="1.75" />
+ <TypeIdentifier>
+ <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
+ <FileName>Consumer.cs</FileName>
+ </TypeIdentifier>
+ </Class>
+ <Class Name="DotNetOAuth.DirectMessageChannel">
+ <Position X="5" Y="2.75" Width="2" />
+ <TypeIdentifier>
+ <HashCode>AAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
+ <FileName>DirectMessageChannel.cs</FileName>
+ </TypeIdentifier>
+ </Class>
+ <Class Name="DotNetOAuth.IndirectMessageEncoder">
+ <Position X="5" Y="4" Width="2" />
+ <TypeIdentifier>
+ <HashCode>AAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
+ <FileName>IndirectMessageEncoder.cs</FileName>
+ </TypeIdentifier>
+ </Class>
+ <Class Name="DotNetOAuth.IndirectMessage" Collapsed="true">
+ <Position X="5" Y="5.25" Width="2" />
+ <TypeIdentifier>
+ <HashCode>AAAAAAAEAABAAAAAAEABAAAACAAAAAAAAAAAAAAAAAA=</HashCode>
+ <FileName>IndirectMessage.cs</FileName>
+ </TypeIdentifier>
+ </Class>
+ <Class Name="DotNetOAuth.ProtocolMessageSerializer<T>">
+ <Position X="5" Y="0.5" Width="2.25" />
+ <TypeIdentifier>
+ <HashCode>AAAAAAAAIAAAAAAAAAAAAAAEACBAAAAAAAAAAAAAAAA=</HashCode>
+ <FileName>ProtocolMessageSerializer.cs</FileName>
+ </TypeIdentifier>
+ </Class>
+ <Interface Name="DotNetOAuth.IProtocolMessage">
+ <Position X="2.75" Y="1.5" Width="1.75" />
+ <TypeIdentifier>
+ <HashCode>AAAAAAAAQAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA=</HashCode>
+ <FileName>IProtocolMessage.cs</FileName>
+ </TypeIdentifier>
+ </Interface>
+ <Interface Name="DotNetOAuth.IProtocolMessageRequest">
+ <Position X="2.75" Y="3.25" Width="1.75" />
+ <TypeIdentifier>
+ <HashCode>AAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
+ <FileName>IProtocolMessageRequest.cs</FileName>
+ </TypeIdentifier>
+ </Interface>
+ <Enum Name="DotNetOAuth.MessageScheme">
+ <Position X="0.5" Y="2.75" Width="2" />
+ <TypeIdentifier>
+ <HashCode>AQAAAAAAAAAAAAIAAAAAAACAAAAAAAgAAAAAAAAAAAA=</HashCode>
+ <FileName>MessageScheme.cs</FileName>
+ </TypeIdentifier>
+ </Enum>
+ <Enum Name="DotNetOAuth.MessageTransport">
+ <Position X="0.5" Y="4.5" Width="1.75" />
+ <TypeIdentifier>
+ <HashCode>AAACAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
+ <FileName>MessageTransport.cs</FileName>
+ </TypeIdentifier>
+ </Enum>
+ <Font Name="Segoe UI" Size="9" />
+</ClassDiagram>
\ No newline at end of file diff --git a/src/DotNetOAuth/Consumer.cs b/src/DotNetOAuth/Consumer.cs new file mode 100644 index 0000000..1a73d5f --- /dev/null +++ b/src/DotNetOAuth/Consumer.cs @@ -0,0 +1,13 @@ +//-----------------------------------------------------------------------
+// <copyright file="Consumer.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth {
+ /// <summary>
+ /// A website or application that uses OAuth to access the Service Provider on behalf of the User.
+ /// </summary>
+ internal class Consumer {
+ }
+}
diff --git a/src/YOURLIBNAME/YOURLIBNAME.csproj b/src/DotNetOAuth/DotNetOAuth.csproj index dd8721c..4db5eb0 100644 --- a/src/YOURLIBNAME/YOURLIBNAME.csproj +++ b/src/DotNetOAuth/DotNetOAuth.csproj @@ -8,8 +8,8 @@ <ProjectGuid>{3191B653-F76D-4C1A-9A5A-347BC3AAAAB7}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>YOURLIBNAME</RootNamespace>
- <AssemblyName>YOURLIBNAME</AssemblyName>
+ <RootNamespace>DotNetOAuth</RootNamespace>
+ <AssemblyName>DotNetOAuth</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
@@ -22,8 +22,8 @@ <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
- <DocumentationFile>..\..\bin\debug\YOURLIBNAME.xml</DocumentationFile>
- <RunCodeAnalysis>true</RunCodeAnalysis>
+ <DocumentationFile>..\..\bin\debug\DotNetOAuth.xml</DocumentationFile>
+ <RunCodeAnalysis>false</RunCodeAnalysis>
<CodeAnalysisRules>-Microsoft.Design#CA1054;-Microsoft.Design#CA1056;-Microsoft.Design#CA1055</CodeAnalysisRules>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
@@ -34,7 +34,7 @@ <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
- <DocumentationFile>..\..\bin\debug\YOURLIBNAME.xml</DocumentationFile>
+ <DocumentationFile>..\..\bin\debug\DotNetOAuth.xml</DocumentationFile>
<RunCodeAnalysis>true</RunCodeAnalysis>
<CodeAnalysisRules>-Microsoft.Design#CA1054;-Microsoft.Design#CA1056;-Microsoft.Design#CA1055</CodeAnalysisRules>
</PropertyGroup>
@@ -66,14 +66,57 @@ <Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="Consumer.cs" />
+ <Compile Include="Messaging\DictionaryXmlReader.cs" />
+ <Compile Include="Messaging\DictionaryXmlWriter.cs" />
+ <Compile Include="Messaging\Channel.cs" />
+ <Compile Include="Messaging\HttpRequestInfo.cs" />
+ <Compile Include="Messaging\IDirectedProtocolMessage.cs" />
+ <Compile Include="Messaging\IMessageTypeProvider.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="OAuthChannel.cs" />
+ <Compile Include="Messaging\Response.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\MessageScheme.cs" />
+ <Compile Include="Messaging\MessageTransport.cs" />
+ <Compile Include="OAuthMessageTypeProvider.cs" />
+ <Compile Include="ProtocolException.cs" />
+ <Compile Include="Messaging\MessageSerializer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Util.cs" />
+ <Compile Include="Protocol.cs" />
+ <Compile Include="ServiceProvider.cs" />
+ <Compile Include="Strings.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>Strings.resx</DependentUpon>
+ </Compile>
+ <Compile Include="UriUtil.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="ClassDiagram.cd" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="Messaging\MessagingStrings.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>MessagingStrings.Designer.cs</LastGenOutput>
+ </EmbeddedResource>
+ <EmbeddedResource Include="Strings.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Strings.Designer.cs</LastGenOutput>
+ <SubType>Designer</SubType>
+ </EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
- <Import Project="..\..\tools\YOURLIBNAME.Versioning.targets" />
+ <Import Project="..\..\tools\DotNetOAuth.Versioning.targets" />
</Project>
\ No newline at end of file diff --git a/src/YOURLIBNAME/Logger.cs b/src/DotNetOAuth/Logger.cs index f63e8f3..96357a0 100644 --- a/src/YOURLIBNAME/Logger.cs +++ b/src/DotNetOAuth/Logger.cs @@ -4,14 +4,14 @@ // </copyright>
//-----------------------------------------------------------------------
-namespace YOURLIBNAME {
+namespace DotNetOAuth {
using System;
using System.Globalization;
+ using DotNetOAuth.Loggers;
using log4net.Core;
- using YOURLIBNAME.Loggers;
/// <summary>
- /// A general logger for the entire YOURLIBNAME library.
+ /// A general logger for the entire DotNetOAuth library.
/// </summary>
/// <remarks>
/// Because this logger is intended for use with non-localized strings, the
@@ -23,7 +23,7 @@ namespace YOURLIBNAME { /// The <see cref="ILog"/> instance that is to be used
/// by this static Logger for the duration of the appdomain.
/// </summary>
- private static ILog facade = Create("YOURLIBNAME");
+ private static ILog facade = Create("DotNetOAuth");
#region ILog Members
//// Although this static class doesn't literally implement the ILog interface,
diff --git a/src/YOURLIBNAME/Loggers/ILog.cs b/src/DotNetOAuth/Loggers/ILog.cs index 2f5fa82..51e5570 100644 --- a/src/YOURLIBNAME/Loggers/ILog.cs +++ b/src/DotNetOAuth/Loggers/ILog.cs @@ -23,7 +23,7 @@ // hosting web site chooses not to deploy log4net.dll along with
// dotnetopenid.dll.
-namespace YOURLIBNAME.Loggers
+namespace DotNetOAuth.Loggers
{
using System;
using System.Reflection;
diff --git a/src/YOURLIBNAME/Loggers/Log4NetLogger.cs b/src/DotNetOAuth/Loggers/Log4NetLogger.cs index efd5466..49fd5de 100644 --- a/src/YOURLIBNAME/Loggers/Log4NetLogger.cs +++ b/src/DotNetOAuth/Loggers/Log4NetLogger.cs @@ -1,6 +1,6 @@ // <auto-generated />
-namespace YOURLIBNAME.Loggers {
+namespace DotNetOAuth.Loggers {
using System;
using System.Globalization;
using System.IO;
diff --git a/src/YOURLIBNAME/Loggers/NoOpLogger.cs b/src/DotNetOAuth/Loggers/NoOpLogger.cs index 768bd70..2f48b3c 100644 --- a/src/YOURLIBNAME/Loggers/NoOpLogger.cs +++ b/src/DotNetOAuth/Loggers/NoOpLogger.cs @@ -1,6 +1,6 @@ // <auto-generated />
-namespace YOURLIBNAME.Loggers {
+namespace DotNetOAuth.Loggers {
using System;
internal class NoOpLogger : ILog {
diff --git a/src/YOURLIBNAME/Loggers/TraceLogger.cs b/src/DotNetOAuth/Loggers/TraceLogger.cs index 0e48188..c55d6df 100644 --- a/src/YOURLIBNAME/Loggers/TraceLogger.cs +++ b/src/DotNetOAuth/Loggers/TraceLogger.cs @@ -1,6 +1,6 @@ // <auto-generated />
-namespace YOURLIBNAME.Loggers {
+namespace DotNetOAuth.Loggers {
using System;
using System.Diagnostics;
using System.Security;
diff --git a/src/DotNetOAuth/Messaging/Channel.cs b/src/DotNetOAuth/Messaging/Channel.cs new file mode 100644 index 0000000..40261b1 --- /dev/null +++ b/src/DotNetOAuth/Messaging/Channel.cs @@ -0,0 +1,354 @@ +//-----------------------------------------------------------------------
+// <copyright file="Channel.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Net;
+ using System.Text;
+ using System.Web;
+
+ /// <summary>
+ /// Manages sending direct messages to a remote party and receiving responses.
+ /// </summary>
+ internal 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 IMessageTypeProvider messageTypeProvider;
+
+ /// <summary>
+ /// Gets or sets the HTTP response to send as a reply to the current incoming HTTP request.
+ /// </summary>
+ private Response queuedIndirectOrResponseMessage;
+
+ /// <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>
+ protected Channel(IMessageTypeProvider messageTypeProvider) {
+ if (messageTypeProvider == null) {
+ throw new ArgumentNullException("messageTypeProvider");
+ }
+
+ this.messageTypeProvider = messageTypeProvider;
+ }
+
+ /// <summary>
+ /// Gets a tool that can figure out what kind of message is being received
+ /// so it can be deserialized.
+ /// </summary>
+ protected IMessageTypeProvider MessageTypeProvider {
+ get { return this.messageTypeProvider; }
+ }
+
+ /// <summary>
+ /// Retrieves the stored response for sending and clears it from the channel.
+ /// </summary>
+ /// <returns>The response to send as the HTTP response.</returns>
+ internal Response DequeueIndirectOrResponseMessage() {
+ Response response = this.queuedIndirectOrResponseMessage;
+ this.queuedIndirectOrResponseMessage = null;
+ return response;
+ }
+
+ /// <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>
+ internal void Send(IProtocolMessage message) {
+ this.Send(message, null);
+ }
+
+ /// <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>
+ /// <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>
+ internal void Send(IProtocolMessage message, IProtocolMessage inResponseTo) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ var directedMessage = message as IDirectedProtocolMessage;
+ if (directedMessage == null) {
+ // This is a response to a direct message.
+ this.SendDirectMessageResponse(message);
+ } else {
+ if (directedMessage.Recipient != null) {
+ // This is an indirect message request or reply.
+ this.SendIndirectMessage(directedMessage);
+ } else {
+ ProtocolException exception = message as ProtocolException;
+ if (exception != null) {
+ if (inResponseTo is IDirectedProtocolMessage) {
+ this.ReportErrorAsDirectResponse(exception);
+ } else {
+ this.ReportErrorToUser(exception);
+ }
+ } else {
+ throw new InvalidOperationException(MessagingStrings.DirectedMessageMissingRecipient);
+ }
+ }
+ }
+ }
+
+ /// <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>
+ internal IProtocolMessage ReadFromRequest() {
+ return this.ReadFromRequest(new HttpRequestInfo(HttpContext.Current.Request));
+ }
+
+ /// <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 internal virtual IProtocolMessage ReadFromRequest(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 this.Receive(fields);
+ }
+
+ /// <summary>
+ /// Gets the protocol message that may be in the given HTTP response stream.
+ /// </summary>
+ /// <param name="responseStream">The response that is anticipated to contain an OAuth message.</param>
+ /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
+ protected internal abstract IProtocolMessage ReadFromResponse(Stream responseStream);
+
+ /// <summary>
+ /// Sends a direct message to a remote party and waits for the response.
+ /// </summary>
+ /// <param name="request">The message to send.</param>
+ /// <returns>The remote party's response.</returns>
+ protected internal abstract IProtocolMessage Request(IDirectedProtocolMessage request);
+
+ /// <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>
+ /// <returns>The deserialized message.</returns>
+ protected virtual IProtocolMessage Receive(Dictionary<string, string> fields) {
+ Type messageType = null;
+ if (fields != null) {
+ messageType = this.MessageTypeProvider.GetRequestMessageType(fields);
+ }
+
+ // If there was no data, or we couldn't recognize it as a message, abort.
+ if (messageType == null) {
+ return null;
+ }
+
+ // We have a message! Assemble it.
+ var serializer = MessageSerializer.Get(messageType);
+ IProtocolMessage message = serializer.Deserialize(fields);
+
+ return message;
+ }
+
+ /// <summary>
+ /// Takes a message and temporarily stores it for sending as the hosting site's
+ /// HTTP response to the current request.
+ /// </summary>
+ /// <param name="response">The message to store for sending.</param>
+ protected void QueueIndirectOrResponseMessage(Response response) {
+ if (response == null) {
+ throw new ArgumentNullException("response");
+ }
+ if (this.queuedIndirectOrResponseMessage != null) {
+ throw new InvalidOperationException(MessagingStrings.QueuedMessageResponseAlreadyExists);
+ }
+
+ this.queuedIndirectOrResponseMessage = response;
+ }
+
+ /// <summary>
+ /// Queues an indirect message for transmittal via the user agent.
+ /// </summary>
+ /// <param name="message">The message to send.</param>
+ protected virtual void SendIndirectMessage(IDirectedProtocolMessage message) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ var serializer = MessageSerializer.Get(message.GetType());
+ var fields = serializer.Serialize(message);
+ Response response;
+ if (CalculateSizeOfPayload(fields) > indirectMessageGetToPostThreshold) {
+ response = this.CreateFormPostResponse(message, fields);
+ } else {
+ response = this.Create301RedirectResponse(message, fields);
+ }
+
+ this.QueueIndirectOrResponseMessage(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 Response Create301RedirectResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields) {
+ if (message == null) {
+ throw new ArgumentNullException("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);
+ Response response = new Response {
+ Status = HttpStatusCode.Redirect,
+ Headers = headers,
+ Body = new byte[0],
+ 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 Response CreateFormPostResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+
+ WebHeaderCollection headers = new WebHeaderCollection();
+ MemoryStream body = new MemoryStream();
+ StreamWriter bodyWriter = new StreamWriter(body);
+ 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();
+ Response response = new Response {
+ Status = HttpStatusCode.Redirect,
+ Headers = headers,
+ Body = body.ToArray(),
+ OriginalMessage = message
+ };
+
+ return response;
+ }
+
+ /// <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>
+ /// <remarks>
+ /// This method implements spec V1.0 section 5.3.
+ /// </remarks>
+ protected abstract void SendDirectMessageResponse(IProtocolMessage response);
+
+ /// <summary>
+ /// Reports an error to the user via the user agent.
+ /// </summary>
+ /// <param name="exception">The error information.</param>
+ protected abstract void ReportErrorToUser(ProtocolException exception);
+
+ /// <summary>
+ /// Sends an error result directly to the calling remote party according to the
+ /// rules of the protocol.
+ /// </summary>
+ /// <param name="exception">The error information.</param>
+ protected abstract void ReportErrorAsDirectResponse(ProtocolException exception);
+
+ /// <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) {
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+
+ int size = 0;
+ foreach (var field in fields) {
+ size += field.Key.Length;
+ size += field.Value.Length;
+ size += 2; // & and =
+ }
+ return size;
+ }
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/DictionaryXmlReader.cs b/src/DotNetOAuth/Messaging/DictionaryXmlReader.cs new file mode 100644 index 0000000..7a5dc21 --- /dev/null +++ b/src/DotNetOAuth/Messaging/DictionaryXmlReader.cs @@ -0,0 +1,91 @@ +//-----------------------------------------------------------------------
+// <copyright file="DictionaryXmlReader.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.IO;
+ using System.Linq;
+ using System.Xml;
+ using System.Xml.Linq;
+
+ /// <summary>
+ /// An XmlReader-looking object that actually reads from a dictionary.
+ /// </summary>
+ internal class DictionaryXmlReader {
+ /// <summary>
+ /// Creates an XmlReader that reads data out of a dictionary instead of XML.
+ /// </summary>
+ /// <param name="rootElement">The name of the root XML element.</param>
+ /// <param name="fields">The dictionary to read data from.</param>
+ /// <returns>The XmlReader that will read the data out of the given dictionary.</returns>
+ internal static XmlReader Create(XName rootElement, IDictionary<string, string> fields) {
+ if (rootElement == null) {
+ throw new ArgumentNullException("rootElement");
+ }
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+
+ return CreateRoundtripReader(rootElement, fields);
+ }
+
+ /// <summary>
+ /// Creates an <see cref="XmlReader"/> that will read values out of a dictionary.
+ /// </summary>
+ /// <param name="rootElement">The surrounding root XML element to generate.</param>
+ /// <param name="fields">The dictionary to list values from.</param>
+ /// <returns>The generated <see cref="XmlReader"/>.</returns>
+ private static XmlReader CreateRoundtripReader(XName rootElement, IDictionary<string, string> fields) {
+ if (rootElement == null) {
+ throw new ArgumentNullException("rootElement");
+ }
+
+ MemoryStream stream = new MemoryStream();
+ XmlWriter writer = XmlWriter.Create(stream);
+ SerializeDictionaryToXml(writer, rootElement, fields);
+ writer.Flush();
+ stream.Seek(0, SeekOrigin.Begin);
+
+ // For debugging purposes.
+ StreamReader sr = new StreamReader(stream);
+ Trace.WriteLine(sr.ReadToEnd());
+ stream.Seek(0, SeekOrigin.Begin);
+
+ return XmlReader.Create(stream);
+ }
+
+ /// <summary>
+ /// Writes out the values in a dictionary as XML.
+ /// </summary>
+ /// <param name="writer">The <see cref="XmlWriter"/> to write out the XML to.</param>
+ /// <param name="rootElement">The name of the root element to use to surround the dictionary values.</param>
+ /// <param name="fields">The dictionary with values to serialize.</param>
+ private static void SerializeDictionaryToXml(XmlWriter writer, XName rootElement, IDictionary<string, string> fields) {
+ if (writer == null) {
+ throw new ArgumentNullException("writer");
+ }
+ if (rootElement == null) {
+ throw new ArgumentNullException("rootElement");
+ }
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+
+ writer.WriteStartElement(rootElement.LocalName, rootElement.NamespaceName);
+
+ // The elements must be serialized in alphabetical order so the DataContractSerializer will see them.
+ foreach (var pair in fields.OrderBy(pair => pair.Key, StringComparer.Ordinal)) {
+ writer.WriteStartElement(pair.Key, rootElement.NamespaceName);
+ writer.WriteValue(pair.Value);
+ writer.WriteEndElement();
+ }
+
+ writer.WriteEndElement();
+ }
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/DictionaryXmlWriter.cs b/src/DotNetOAuth/Messaging/DictionaryXmlWriter.cs new file mode 100644 index 0000000..3be043f --- /dev/null +++ b/src/DotNetOAuth/Messaging/DictionaryXmlWriter.cs @@ -0,0 +1,273 @@ +//-----------------------------------------------------------------------
+// <copyright file="DictionaryXmlWriter.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+ using System.Xml;
+
+ /// <summary>
+ /// An XmlWriter-looking object that actually saves data to a dictionary.
+ /// </summary>
+ internal class DictionaryXmlWriter {
+ /// <summary>
+ /// Creates an <see cref="XmlWriter"/> that actually writes to an IDictionary<string, string> instance.
+ /// </summary>
+ /// <param name="dictionary">The dictionary to save the written XML to.</param>
+ /// <returns>The XmlWriter that will save data to the given dictionary.</returns>
+ internal static XmlWriter Create(IDictionary<string, string> dictionary) {
+ return new PseudoXmlWriter(dictionary);
+ }
+
+ /// <summary>
+ /// Writes out a dictionary as if it were XML.
+ /// </summary>
+ private class PseudoXmlWriter : XmlWriter {
+ /// <summary>
+ /// The dictionary to write values to.
+ /// </summary>
+ private IDictionary<string, string> dictionary;
+
+ /// <summary>
+ /// The key being written at the moment.
+ /// </summary>
+ private string key;
+
+ /// <summary>
+ /// The value being written out at the moment.
+ /// </summary>
+ private StringBuilder value = new StringBuilder();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PseudoXmlWriter"/> class.
+ /// </summary>
+ /// <param name="dictionary">The dictionary that will be written to.</param>
+ internal PseudoXmlWriter(IDictionary<string, string> dictionary) {
+ if (dictionary == null) {
+ throw new ArgumentNullException("dictionary");
+ }
+
+ this.dictionary = dictionary;
+ }
+
+ /// <summary>
+ /// Gets the spoofed state of the <see cref="XmlWriter"/>.
+ /// </summary>
+ public override WriteState WriteState {
+ get { return WriteState.Element; }
+ }
+
+ /// <summary>
+ /// Prepares to write out a new key/value pair with the given key name to the dictionary.
+ /// </summary>
+ /// <param name="prefix">This parameter is ignored.</param>
+ /// <param name="localName">The key to store in the dictionary.</param>
+ /// <param name="ns">This parameter is ignored.</param>
+ public override void WriteStartElement(string prefix, string localName, string ns) {
+ this.key = localName;
+ this.value.Length = 0;
+ }
+
+ /// <summary>
+ /// Appends some text to the value that is to be stored in the dictionary.
+ /// </summary>
+ /// <param name="text">The text to append to the value.</param>
+ public override void WriteString(string text) {
+ if (!string.IsNullOrEmpty(this.key)) {
+ this.value.Append(text);
+ }
+ }
+
+ /// <summary>
+ /// Writes out a completed key/value to the dictionary.
+ /// </summary>
+ public override void WriteEndElement() {
+ if (this.key != null) {
+ this.dictionary[this.key] = this.value.ToString();
+ this.key = null;
+ this.value.Length = 0;
+ }
+ }
+
+ /// <summary>
+ /// Clears the internal key/value building state.
+ /// </summary>
+ /// <param name="prefix">This parameter is ignored.</param>
+ /// <param name="localName">This parameter is ignored.</param>
+ /// <param name="ns">This parameter is ignored.</param>
+ public override void WriteStartAttribute(string prefix, string localName, string ns) {
+ this.key = null;
+ }
+
+ /// <summary>
+ /// This method does not do anything.
+ /// </summary>
+ public override void WriteEndAttribute() { }
+
+ /// <summary>
+ /// This method does not do anything.
+ /// </summary>
+ public override void Close() { }
+
+ #region Unimplemented methods
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ public override void Flush() {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="ns">This parameter is ignored.</param>
+ /// <returns>None, since an exception is always thrown.</returns>
+ public override string LookupPrefix(string ns) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="buffer">This parameter is ignored.</param>
+ /// <param name="index">This parameter is ignored.</param>
+ /// <param name="count">This parameter is ignored.</param>
+ public override void WriteBase64(byte[] buffer, int index, int count) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="text">This parameter is ignored.</param>
+ public override void WriteCData(string text) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="ch">This parameter is ignored.</param>
+ public override void WriteCharEntity(char ch) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="buffer">This parameter is ignored.</param>
+ /// <param name="index">This parameter is ignored.</param>
+ /// <param name="count">This parameter is ignored.</param>
+ public override void WriteChars(char[] buffer, int index, int count) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="text">This parameter is ignored.</param>
+ public override void WriteComment(string text) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="name">This parameter is ignored.</param>
+ /// <param name="pubid">This parameter is ignored.</param>
+ /// <param name="sysid">This parameter is ignored.</param>
+ /// <param name="subset">This parameter is ignored.</param>
+ public override void WriteDocType(string name, string pubid, string sysid, string subset) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ public override void WriteEndDocument() {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="name">This parameter is ignored.</param>
+ public override void WriteEntityRef(string name) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ public override void WriteFullEndElement() {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="name">This parameter is ignored.</param>
+ /// <param name="text">This parameter is ignored.</param>
+ public override void WriteProcessingInstruction(string name, string text) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="data">This parameter is ignored.</param>
+ public override void WriteRaw(string data) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="buffer">This parameter is ignored.</param>
+ /// <param name="index">This parameter is ignored.</param>
+ /// <param name="count">This parameter is ignored.</param>
+ public override void WriteRaw(char[] buffer, int index, int count) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="standalone">This parameter is ignored.</param>
+ public override void WriteStartDocument(bool standalone) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ public override void WriteStartDocument() {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="lowChar">This parameter is ignored.</param>
+ /// <param name="highChar">This parameter is ignored.</param>
+ public override void WriteSurrogateCharEntity(char lowChar, char highChar) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Throws <see cref="NotImplementedException"/>.
+ /// </summary>
+ /// <param name="ws">This parameter is ignored.</param>
+ public override void WriteWhitespace(string ws) {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/HttpRequestInfo.cs b/src/DotNetOAuth/Messaging/HttpRequestInfo.cs new file mode 100644 index 0000000..3654367 --- /dev/null +++ b/src/DotNetOAuth/Messaging/HttpRequestInfo.cs @@ -0,0 +1,137 @@ +//-----------------------------------------------------------------------
+// <copyright file="HttpRequestInfo.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ using System;
+ using System.Collections.Specialized;
+ using System.IO;
+ using System.Net;
+ using System.Web;
+
+ /// <summary>
+ /// A property store of details of an incoming HTTP request.
+ /// </summary>
+ /// <remarks>
+ /// This serves a very similar purpose to <see cref="HttpRequest"/>, except that
+ /// ASP.NET does not let us fully initialize that class, so we have to write one
+ /// of our one.
+ /// </remarks>
+ internal class HttpRequestInfo {
+ /// <summary>
+ /// The key/value pairs found in the entity of a POST request.
+ /// </summary>
+ private NameValueCollection form;
+
+ /// <summary>
+ /// The key/value pairs found in the querystring of the incoming request.
+ /// </summary>
+ private NameValueCollection queryString;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HttpRequestInfo"/> class.
+ /// </summary>
+ internal HttpRequestInfo() {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HttpRequestInfo"/> class.
+ /// </summary>
+ /// <param name="request">The ASP.NET structure to copy from.</param>
+ internal HttpRequestInfo(HttpRequest request) {
+ this.HttpMethod = request.HttpMethod;
+ this.Url = request.Url;
+ this.Headers = GetHeaderCollection(request.Headers);
+ this.InputStream = request.InputStream;
+
+ // These values would normally be calculated, but we'll reuse them from
+ // HttpRequest since they're already calculated, and there's a chance (<g>)
+ // that ASP.NET does a better job of being comprehensive about gathering
+ // these as well.
+ this.form = request.Form;
+ this.queryString = request.QueryString;
+ }
+
+ /// <summary>
+ /// Gets or sets the verb in the request (i.e. GET, POST, etc.)
+ /// </summary>
+ internal string HttpMethod { get; set; }
+
+ /// <summary>
+ /// Gets or sets the entire URL of the request.
+ /// </summary>
+ internal Uri Url { get; set; }
+
+ /// <summary>
+ /// Gets the query part of the URL (The ? and everything after it).
+ /// </summary>
+ internal string Query {
+ get { return this.Url != null ? this.Url.Query : null; }
+ }
+
+ /// <summary>
+ /// Gets or sets the collection of headers that came in with the request.
+ /// </summary>
+ internal WebHeaderCollection Headers { get; set; }
+
+ /// <summary>
+ /// Gets or sets the entity, or body of the request, if any.
+ /// </summary>
+ internal Stream InputStream { get; set; }
+
+ /// <summary>
+ /// Gets the key/value pairs found in the entity of a POST request.
+ /// </summary>
+ internal NameValueCollection Form {
+ get {
+ if (this.form == null) {
+ if (this.HttpMethod == "POST" && this.Headers[HttpRequestHeader.ContentType] == "application/x-www-form-urlencoded") {
+ StreamReader reader = new StreamReader(this.InputStream);
+ long originalPosition = this.InputStream.Position;
+ this.form = HttpUtility.ParseQueryString(reader.ReadToEnd());
+ if (this.InputStream.CanSeek) {
+ this.InputStream.Seek(originalPosition, SeekOrigin.Begin);
+ }
+ } else {
+ this.form = new NameValueCollection();
+ }
+ }
+
+ return this.form;
+ }
+ }
+
+ /// <summary>
+ /// Gets the key/value pairs found in the querystring of the incoming request.
+ /// </summary>
+ internal NameValueCollection QueryString {
+ get {
+ if (this.queryString == null) {
+ this.queryString = HttpUtility.ParseQueryString(this.Query);
+ }
+
+ return this.queryString;
+ }
+ }
+
+ /// <summary>
+ /// Converts a NameValueCollection to a WebHeaderCollection.
+ /// </summary>
+ /// <param name="pairs">The collection a HTTP headers.</param>
+ /// <returns>A new collection of the given headers.</returns>
+ private static WebHeaderCollection GetHeaderCollection(NameValueCollection pairs) {
+ if (pairs == null) {
+ throw new ArgumentNullException("pairs");
+ }
+
+ WebHeaderCollection headers = new WebHeaderCollection();
+ foreach (string key in pairs) {
+ headers.Add(key, pairs[key]);
+ }
+
+ return headers;
+ }
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/IDirectedProtocolMessage.cs b/src/DotNetOAuth/Messaging/IDirectedProtocolMessage.cs new file mode 100644 index 0000000..bebd303 --- /dev/null +++ b/src/DotNetOAuth/Messaging/IDirectedProtocolMessage.cs @@ -0,0 +1,20 @@ +//-----------------------------------------------------------------------
+// <copyright file="IDirectedProtocolMessage.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ using System;
+
+ /// <summary>
+ /// Implemented by messages that have explicit recipients
+ /// (direct requests and all indirect messages).
+ /// </summary>
+ internal interface IDirectedProtocolMessage : IProtocolMessage {
+ /// <summary>
+ /// Gets the URL of the intended receiver of this message.
+ /// </summary>
+ Uri Recipient { get; }
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/IMessageTypeProvider.cs b/src/DotNetOAuth/Messaging/IMessageTypeProvider.cs new file mode 100644 index 0000000..17affcb --- /dev/null +++ b/src/DotNetOAuth/Messaging/IMessageTypeProvider.cs @@ -0,0 +1,41 @@ +//-----------------------------------------------------------------------
+// <copyright file="IMessageTypeProvider.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+
+ /// <summary>
+ /// A tool to analyze an incoming message to figure out what concrete class
+ /// is designed to deserialize it.
+ /// </summary>
+ internal interface IMessageTypeProvider {
+ /// <summary>
+ /// Analyzes an incoming request message payload to discover what kind of
+ /// message is embedded in it and returns the type, or null if no match is found.
+ /// </summary>
+ /// <param name="fields">The name/value pairs that make up the message payload.</param>
+ /// <returns>
+ /// The <see cref="IProtocolMessage"/>-derived concrete class that this message can
+ /// deserialize to. Null if the request isn't recognized as a valid protocol message.
+ /// </returns>
+ Type GetRequestMessageType(IDictionary<string, string> fields);
+
+ /// <summary>
+ /// Analyzes an incoming request message payload to discover what kind of
+ /// message is embedded in it and returns the type, or null if no match is found.
+ /// </summary>
+ /// <param name="request">
+ /// The message that was sent as a request that resulted in the response.
+ /// </param>
+ /// <param name="fields">The name/value pairs that make up the message payload.</param>
+ /// <returns>
+ /// The <see cref="IProtocolMessage"/>-derived concrete class that this message can
+ /// deserialize to. Null if the request isn't recognized as a valid protocol message.
+ /// </returns>
+ Type GetResponseMessageType(IProtocolMessage request, IDictionary<string, string> fields);
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/IProtocolMessage.cs b/src/DotNetOAuth/Messaging/IProtocolMessage.cs new file mode 100644 index 0000000..3d9f82e --- /dev/null +++ b/src/DotNetOAuth/Messaging/IProtocolMessage.cs @@ -0,0 +1,42 @@ +//-----------------------------------------------------------------------
+// <copyright file="IProtocolMessage.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.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>
+ internal interface IProtocolMessage {
+ /// <summary>
+ /// Gets the version of the protocol this message is prepared to implement.
+ /// </summary>
+ Protocol Protocol { get; }
+
+ /// <summary>
+ /// Gets whether this is a direct or indirect message.
+ /// </summary>
+ [Obsolete("Are we using this anywhere?")]
+ MessageTransport Transport { 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/DotNetOAuth/Messaging/MessageScheme.cs b/src/DotNetOAuth/Messaging/MessageScheme.cs new file mode 100644 index 0000000..ee91740 --- /dev/null +++ b/src/DotNetOAuth/Messaging/MessageScheme.cs @@ -0,0 +1,35 @@ +//-----------------------------------------------------------------------
+// <copyright file="MessageScheme.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ /// <summary>
+ /// The methods available for the Consumer to send direct messages to the Service Provider.
+ /// </summary>
+ /// <remarks>
+ /// See 1.0 spec section 5.2.
+ /// </remarks>
+ internal enum MessageScheme {
+ /// <summary>
+ /// In the HTTP Authorization header as defined in OAuth HTTP Authorization Scheme (OAuth HTTP Authorization Scheme).
+ /// </summary>
+ AuthorizationHeaderRequest,
+
+ /// <summary>
+ /// As the HTTP POST request body with a content-type of application/x-www-form-urlencoded.
+ /// </summary>
+ PostRequest,
+
+ /// <summary>
+ /// Added to the URLs in the query part (as defined by [RFC3986] (Berners-Lee, T., “Uniform Resource Identifiers (URI): Generic Syntax,” .) section 3).
+ /// </summary>
+ GetRequest,
+
+ /// <summary>
+ /// Response parameters are sent by the Service Provider to return Tokens and other information to the Consumer in the HTTP response body. The parameter names and values are first encoded as per Parameter Encoding (Parameter Encoding), and concatenated with the ‘&’ character (ASCII code 38) as defined in [RFC3986] (Berners-Lee, T., “Uniform Resource Identifiers (URI): Generic Syntax,” .) Section 2.1.
+ /// </summary>
+ QueryStyleResponse,
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/MessageSerializer.cs b/src/DotNetOAuth/Messaging/MessageSerializer.cs new file mode 100644 index 0000000..ac40232 --- /dev/null +++ b/src/DotNetOAuth/Messaging/MessageSerializer.cs @@ -0,0 +1,167 @@ +//-----------------------------------------------------------------------
+// <copyright file="MessageSerializer.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Linq;
+ using System.Runtime.Serialization;
+ using System.Xml;
+ using System.Xml.Linq;
+
+ /// <summary>
+ /// Serializes/deserializes OAuth messages for/from transit.
+ /// </summary>
+ internal class MessageSerializer {
+ /// <summary>
+ /// The serializer that will be used as a reflection engine to extract
+ /// the OAuth message properties out of their containing <see cref="IProtocolMessage"/>
+ /// objects.
+ /// </summary>
+ private readonly DataContractSerializer serializer;
+
+ /// <summary>
+ /// The specific <see cref="IProtocolMessage"/>-derived type
+ /// that will be serialized and deserialized using this class.
+ /// </summary>
+ private readonly Type messageType;
+
+ /// <summary>
+ /// An AppDomain-wide cache of shared serializers for optimization purposes.
+ /// </summary>
+ private static Dictionary<Type, MessageSerializer> prebuiltSerializers = new Dictionary<Type, MessageSerializer>();
+
+ /// <summary>
+ /// Backing field for the <see cref="RootElement"/> property
+ /// </summary>
+ private XName rootElement;
+
+ /// <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) {
+ if (messageType == null) {
+ throw new ArgumentNullException("messageType");
+ }
+ if (!typeof(IProtocolMessage).IsAssignableFrom(messageType)) {
+ throw new ArgumentException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.UnexpectedType,
+ typeof(IProtocolMessage).FullName,
+ messageType.FullName),
+ "messageType");
+ }
+
+ this.messageType = messageType;
+ this.serializer = new DataContractSerializer(
+ messageType, this.RootElement.LocalName, this.RootElement.NamespaceName);
+ }
+
+ /// <summary>
+ /// Gets the XML element that is used to surround all the XML values from the dictionary.
+ /// </summary>
+ private XName RootElement {
+ get {
+ if (this.rootElement == null) {
+ DataContractAttribute attribute = this.messageType.GetCustomAttributes(typeof(DataContractAttribute), false).OfType<DataContractAttribute>().Single();
+ this.rootElement = XName.Get("root", attribute.Namespace);
+ }
+
+ return this.rootElement;
+ }
+ }
+
+ /// <summary>
+ /// Returns a message serializer from a reusable collection of serializers.
+ /// </summary>
+ /// <param name="messageType">The type of message that will be serialized/deserialized.</param>
+ /// <returns>A previously created serializer if one exists, or a newly created one.</returns>
+ internal static MessageSerializer Get(Type messageType) {
+ if (messageType == null) {
+ throw new ArgumentNullException("messageType");
+ }
+
+ // We do this as efficiently as possible by first trying to fetch the
+ // serializer out of the dictionary without taking a lock.
+ MessageSerializer serializer;
+ if (prebuiltSerializers.TryGetValue(messageType, out serializer)) {
+ return serializer;
+ }
+
+ // Since it wasn't there, we'll be trying to write to the dictionary so
+ // we take a lock and try reading again first, then creating the serializer
+ // and storing it when we're sure it absolutely necessary.
+ lock (prebuiltSerializers) {
+ if (prebuiltSerializers.TryGetValue(messageType, out serializer)) {
+ return serializer;
+ }
+ serializer = new MessageSerializer(messageType);
+ prebuiltSerializers.Add(messageType, serializer);
+ }
+ return serializer;
+ }
+
+ /// <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>
+ internal IDictionary<string, string> Serialize(IProtocolMessage message) {
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ var fields = new Dictionary<string, string>(StringComparer.Ordinal);
+ this.Serialize(fields, message);
+ return fields;
+ }
+
+ /// <summary>
+ /// Saves the [DataMember] properties of a message to an existing dictionary.
+ /// </summary>
+ /// <param name="fields">The dictionary to save values to.</param>
+ /// <param name="message">The message to pull values from.</param>
+ internal void Serialize(IDictionary<string, string> fields, IProtocolMessage message) {
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+ if (message == null) {
+ throw new ArgumentNullException("message");
+ }
+
+ message.EnsureValidMessage();
+ using (XmlWriter writer = DictionaryXmlWriter.Create(fields)) {
+ this.serializer.WriteObjectContent(writer, message);
+ }
+ }
+
+ /// <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>
+ /// <returns>The instantiated and initialized <see cref="IProtocolMessage"/> instance.</returns>
+ internal IProtocolMessage Deserialize(IDictionary<string, string> fields) {
+ if (fields == null) {
+ throw new ArgumentNullException("fields");
+ }
+
+ var reader = DictionaryXmlReader.Create(this.RootElement, fields);
+ IProtocolMessage result;
+ try {
+ result = (IProtocolMessage)this.serializer.ReadObject(reader, false);
+ } catch (SerializationException ex) {
+ // Missing required fields is one cause of this exception.
+ throw new ProtocolException(Strings.InvalidIncomingMessage, ex);
+ }
+ result.EnsureValidMessage();
+ return result;
+ }
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/MessageTransport.cs b/src/DotNetOAuth/Messaging/MessageTransport.cs new file mode 100644 index 0000000..40e6897 --- /dev/null +++ b/src/DotNetOAuth/Messaging/MessageTransport.cs @@ -0,0 +1,22 @@ +//-----------------------------------------------------------------------
+// <copyright file="MessageTransport.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ /// <summary>
+ /// The type of transport mechanism used for a message: either direct or indirect.
+ /// </summary>
+ public enum MessageTransport {
+ /// <summary>
+ /// A message that is sent directly from the Consumer to the Service Provider, or vice versa.
+ /// </summary>
+ Direct,
+
+ /// <summary>
+ /// A message that is sent from one party to another via a redirect in the user agent.
+ /// </summary>
+ Indirect,
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs b/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs new file mode 100644 index 0000000..98e8cc6 --- /dev/null +++ b/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs @@ -0,0 +1,108 @@ +//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.3053
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ using System;
+
+
+ /// <summary>
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ /// </summary>
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class MessagingStrings {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal MessagingStrings() {
+ }
+
+ /// <summary>
+ /// Returns the cached ResourceManager instance used by this class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DotNetOAuth.Messaging.MessagingStrings", typeof(MessagingStrings).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ /// <summary>
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to An instance of type {0} was expected, but received unexpected derived type {1}..
+ /// </summary>
+ internal static string DerivedTypeNotExpected {
+ get {
+ return ResourceManager.GetString("DerivedTypeNotExpected", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The directed message's Recipient property must not be null..
+ /// </summary>
+ internal static string DirectedMessageMissingRecipient {
+ get {
+ return ResourceManager.GetString("DirectedMessageMissingRecipient", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Error occurred while sending a direct message or gettings the response..
+ /// </summary>
+ internal static string ErrorInRequestReplyMessage {
+ get {
+ return ResourceManager.GetString("ErrorInRequestReplyMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to A message response is already queued for sending in the response stream..
+ /// </summary>
+ internal static string QueuedMessageResponseAlreadyExists {
+ get {
+ return ResourceManager.GetString("QueuedMessageResponseAlreadyExists", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The type {0} or a derived type was expected, but {1} was given..
+ /// </summary>
+ internal static string UnexpectedType {
+ get {
+ return ResourceManager.GetString("UnexpectedType", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/MessagingStrings.resx b/src/DotNetOAuth/Messaging/MessagingStrings.resx new file mode 100644 index 0000000..2125e15 --- /dev/null +++ b/src/DotNetOAuth/Messaging/MessagingStrings.resx @@ -0,0 +1,135 @@ +<?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="DerivedTypeNotExpected" xml:space="preserve">
+ <value>An instance of type {0} was expected, but received unexpected derived type {1}.</value>
+ </data>
+ <data name="DirectedMessageMissingRecipient" xml:space="preserve">
+ <value>The directed message's Recipient property must not be null.</value>
+ </data>
+ <data name="ErrorInRequestReplyMessage" xml:space="preserve">
+ <value>Error occurred while sending a direct message or gettings the response.</value>
+ </data>
+ <data name="QueuedMessageResponseAlreadyExists" xml:space="preserve">
+ <value>A message response is already queued for sending in the response stream.</value>
+ </data>
+ <data name="UnexpectedType" xml:space="preserve">
+ <value>The type {0} or a derived type was expected, but {1} was given.</value>
+ </data>
+</root>
\ No newline at end of file diff --git a/src/DotNetOAuth/Messaging/MessagingUtilities.cs b/src/DotNetOAuth/Messaging/MessagingUtilities.cs new file mode 100644 index 0000000..e8069f6 --- /dev/null +++ b/src/DotNetOAuth/Messaging/MessagingUtilities.cs @@ -0,0 +1,119 @@ +//-----------------------------------------------------------------------
+// <copyright file="MessagingUtilities.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+ using System.Net;
+ using System.Text;
+ using System.Web;
+
+ /// <summary>
+ /// A grab-bag of utility methods useful for the channel stack of the protocol.
+ /// </summary>
+ internal static class MessagingUtilities {
+ /// <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>
+ /// 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>
+ /// 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;
+ }
+ }
+}
diff --git a/src/DotNetOAuth/Messaging/Response.cs b/src/DotNetOAuth/Messaging/Response.cs new file mode 100644 index 0000000..68950ff --- /dev/null +++ b/src/DotNetOAuth/Messaging/Response.cs @@ -0,0 +1,73 @@ +//-----------------------------------------------------------------------
+// <copyright file="Response.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth.Messaging {
+ using System;
+ using System.Net;
+ using System.Web;
+
+ /// <summary>
+ /// A protocol message (request or response) that passes between Consumer and Service Provider
+ /// via the user agent using a redirect or form POST submission,
+ /// OR a direct message response.
+ /// </summary>
+ /// <remarks>
+ /// <para>An instance of this type describes the HTTP response that must be sent
+ /// in response to the current HTTP request.</para>
+ /// <para>It is important that this response make up the entire HTTP response.
+ /// A hosting ASPX page should not be allowed to render its normal HTML output
+ /// after this response is sent. The normal rendered output of an ASPX page
+ /// can be canceled by calling <see cref="HttpResponse.End"/> after this message
+ /// is sent on the response stream.</para>
+ /// </remarks>
+ public class Response {
+ /// <summary>
+ /// Gets the headers that must be included in the response to the user agent.
+ /// </summary>
+ /// <remarks>
+ /// The headers in this collection are not meant to be a comprehensive list
+ /// of exactly what should be sent, but are meant to augment whatever headers
+ /// are generally included in a typical response.
+ /// </remarks>
+ public WebHeaderCollection Headers { get; internal set; }
+
+ /// <summary>
+ /// Gets the body of the HTTP response.
+ /// </summary>
+ public byte[] Body { get; internal set; }
+
+ /// <summary>
+ /// Gets the HTTP status code to use in the HTTP response.
+ /// </summary>
+ public HttpStatusCode Status { get; internal set; }
+
+ /// <summary>
+ /// Gets or sets a reference to the actual protocol message that
+ /// is being sent via the user agent.
+ /// </summary>
+ internal IProtocolMessage OriginalMessage { get; set; }
+
+ /// <summary>
+ /// Automatically sends the appropriate response to the user agent.
+ /// Requires a current HttpContext.
+ /// </summary>
+ public void Send() {
+ if (HttpContext.Current == null) {
+ throw new InvalidOperationException(Strings.CurrentHttpContextRequired);
+ }
+
+ HttpContext.Current.Response.Clear();
+ HttpContext.Current.Response.StatusCode = (int)this.Status;
+ MessagingUtilities.ApplyHeadersToResponse(this.Headers, HttpContext.Current.Response);
+ if (this.Body != null && this.Body.Length > 0) {
+ HttpContext.Current.Response.OutputStream.Write(this.Body, 0, this.Body.Length);
+ HttpContext.Current.Response.OutputStream.Flush();
+ }
+ HttpContext.Current.Response.OutputStream.Close();
+ HttpContext.Current.Response.End();
+ }
+ }
+}
diff --git a/src/DotNetOAuth/OAuthChannel.cs b/src/DotNetOAuth/OAuthChannel.cs new file mode 100644 index 0000000..6271954 --- /dev/null +++ b/src/DotNetOAuth/OAuthChannel.cs @@ -0,0 +1,246 @@ +//-----------------------------------------------------------------------
+// <copyright file="OAuthChannel.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth {
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Net;
+ using System.Text;
+ using System.Web;
+ using DotNetOAuth.Messaging;
+
+ /// <summary>
+ /// An OAuth-specific implementation of the <see cref="Channel"/> class.
+ /// </summary>
+ internal class OAuthChannel : Channel {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthChannel"/> class.
+ /// </summary>
+ internal OAuthChannel()
+ : base(new OAuthMessageTypeProvider()) {
+ }
+
+ /// <summary>
+ /// Searches an incoming HTTP request for data that could be used to assemble
+ /// a protocol request message.
+ /// </summary>
+ /// <param name="request">The HTTP request to search.</param>
+ /// <returns>A dictionary of data in the request. Should never be null, but may be empty.</returns>
+ protected internal override IProtocolMessage ReadFromRequest(HttpRequestInfo request) {
+ if (request == null) {
+ throw new ArgumentNullException("request");
+ }
+
+ // First search the Authorization header. Use it exclusively if it's present.
+ string authorization = request.Headers[HttpRequestHeader.Authorization];
+ if (authorization != null) {
+ string[] authorizationSections = authorization.Split(';'); // TODO: is this the right delimiter?
+ string oauthPrefix = Protocol.Default.AuthorizationHeaderScheme + " ";
+
+ // The Authorization header may have multiple uses, and OAuth may be just one of them.
+ // Go through each one looking for an OAuth one.
+ foreach (string auth in authorizationSections) {
+ string trimmedAuth = auth.Trim();
+ if (trimmedAuth.StartsWith(oauthPrefix, StringComparison.Ordinal)) {
+ // We found an Authorization: OAuth header.
+ // Parse it according to the rules in section 5.4.1 of the V1.0 spec.
+ var fields = new Dictionary<string, string>();
+ foreach (string stringPair in trimmedAuth.Substring(oauthPrefix.Length).Split(',')) {
+ string[] keyValueStringPair = stringPair.Trim().Split('=');
+ string key = Uri.UnescapeDataString(keyValueStringPair[0]);
+ string value = Uri.UnescapeDataString(keyValueStringPair[1].Trim('"'));
+ fields.Add(key, value);
+ }
+
+ return this.Receive(fields);
+ }
+ }
+ }
+
+ // We didn't find an OAuth authorization header. Revert to other payload methods.
+ return base.ReadFromRequest(request);
+ }
+
+ /// <summary>
+ /// Gets the protocol message that may be in the given HTTP response stream.
+ /// </summary>
+ /// <param name="responseStream">The response that is anticipated to contain an OAuth message.</param>
+ /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
+ protected internal override IProtocolMessage ReadFromResponse(Stream responseStream) {
+ if (responseStream == null) {
+ throw new ArgumentNullException("responseStream");
+ }
+
+ using (StreamReader reader = new StreamReader(responseStream)) {
+ string response = reader.ReadToEnd();
+ var fields = HttpUtility.ParseQueryString(response).ToDictionary();
+ return Receive(fields);
+ }
+ }
+
+ /// <summary>
+ /// Sends a direct message to a remote party and waits for the response.
+ /// </summary>
+ /// <param name="request">The message to send.</param>
+ /// <returns>The remote party's response.</returns>
+ protected internal override IProtocolMessage Request(IDirectedProtocolMessage request) {
+ if (request == null) {
+ throw new ArgumentNullException("request");
+ }
+
+ HttpWebRequest httpRequest;
+
+ MessageScheme transmissionMethod = MessageScheme.AuthorizationHeaderRequest;
+ switch (transmissionMethod) {
+ case MessageScheme.AuthorizationHeaderRequest:
+ httpRequest = this.InitializeRequestAsAuthHeader(request);
+ break;
+ case MessageScheme.PostRequest:
+ httpRequest = this.InitializeRequestAsPost(request);
+ break;
+ case MessageScheme.GetRequest:
+ httpRequest = this.InitializeRequestAsGet(request);
+ break;
+ default:
+ throw new NotSupportedException();
+ }
+
+ // Submit the request and await the reply.
+ Dictionary<string, string> responseFields;
+ try {
+ using (HttpWebResponse response = (HttpWebResponse)httpRequest.GetResponse()) {
+ using (StreamReader reader = new StreamReader(response.GetResponseStream())) {
+ string queryString = reader.ReadToEnd();
+ responseFields = HttpUtility.ParseQueryString(queryString).ToDictionary();
+ }
+ }
+ } catch (WebException ex) {
+ throw new ProtocolException(MessagingStrings.ErrorInRequestReplyMessage, ex);
+ }
+
+ Type messageType = this.MessageTypeProvider.GetResponseMessageType(request, responseFields);
+ var responseSerialize = MessageSerializer.Get(messageType);
+ var responseMessage = responseSerialize.Deserialize(responseFields);
+
+ return responseMessage;
+ }
+
+ /// <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>
+ /// <remarks>
+ /// This method implements spec V1.0 section 5.3.
+ /// </remarks>
+ protected override void SendDirectMessageResponse(IProtocolMessage response) {
+ MessageSerializer serializer = MessageSerializer.Get(response.GetType());
+ var fields = serializer.Serialize(response);
+ string responseBody = MessagingUtilities.CreateQueryString(fields);
+
+ Response encodedResponse = new Response {
+ Body = Encoding.UTF8.GetBytes(responseBody),
+ OriginalMessage = response,
+ Status = System.Net.HttpStatusCode.OK,
+ Headers = new System.Net.WebHeaderCollection(),
+ };
+ this.QueueIndirectOrResponseMessage(encodedResponse);
+ }
+
+ /// <summary>
+ /// Reports an error to the user via the user agent.
+ /// </summary>
+ /// <param name="exception">The error information.</param>
+ protected override void ReportErrorToUser(ProtocolException exception) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Sends an error result directly to the calling remote party according to the
+ /// rules of the protocol.
+ /// </summary>
+ /// <param name="exception">The error information.</param>
+ protected override void ReportErrorAsDirectResponse(ProtocolException exception) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Prepares to send a request to the Service Provider via the Authorization header.
+ /// </summary>
+ /// <param name="requestMessage">The message to be transmitted to the ServiceProvider.</param>
+ /// <returns>The web request ready to send.</returns>
+ /// <remarks>
+ /// This method implements OAuth 1.0 section 5.2, item #1 (described in section 5.4).
+ /// </remarks>
+ private HttpWebRequest InitializeRequestAsAuthHeader(IDirectedProtocolMessage requestMessage) {
+ var serializer = MessageSerializer.Get(requestMessage.GetType());
+ var fields = serializer.Serialize(requestMessage);
+
+ HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(requestMessage.Recipient);
+
+ StringBuilder authorization = new StringBuilder();
+ authorization.Append(requestMessage.Protocol.AuthorizationHeaderScheme);
+ authorization.Append(" ");
+ foreach (var pair in fields) {
+ string key = Uri.EscapeDataString(pair.Key);
+ string value = Uri.EscapeDataString(pair.Value);
+ authorization.Append(key);
+ authorization.Append("=\"");
+ authorization.Append(value);
+ authorization.Append("\",");
+ }
+ authorization.Length--; // remove trailing comma
+
+ httpRequest.Headers.Add(HttpRequestHeader.Authorization, authorization.ToString());
+
+ 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 implements OAuth 1.0 section 5.2, item #2.
+ /// </remarks>
+ private HttpWebRequest InitializeRequestAsPost(IDirectedProtocolMessage 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 (StreamWriter writer = new StreamWriter(httpRequest.GetRequestStream())) {
+ writer.Write(requestBody);
+ }
+
+ return httpRequest;
+ }
+
+ /// <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 implements OAuth 1.0 section 5.2, item #3.
+ /// </remarks>
+ private HttpWebRequest InitializeRequestAsGet(IDirectedProtocolMessage 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;
+ }
+ }
+}
diff --git a/src/DotNetOAuth/OAuthMessageTypeProvider.cs b/src/DotNetOAuth/OAuthMessageTypeProvider.cs new file mode 100644 index 0000000..7d4dbcd --- /dev/null +++ b/src/DotNetOAuth/OAuthMessageTypeProvider.cs @@ -0,0 +1,52 @@ +//-----------------------------------------------------------------------
+// <copyright file="OAuthMessageTypeProvider.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOAuth.Messaging;
+
+ /// <summary>
+ /// An OAuth-protocol specific implementation of the <see cref="IMessageTypeProvider"/>
+ /// interface.
+ /// </summary>
+ internal class OAuthMessageTypeProvider : IMessageTypeProvider {
+ #region IMessageTypeProvider Members
+
+ /// <summary>
+ /// Analyzes an incoming request message payload to discover what kind of
+ /// message is embedded in it and returns the type, or null if no match is found.
+ /// </summary>
+ /// <param name="fields">The name/value pairs that make up the message payload.</param>
+ /// <returns>
+ /// The <see cref="IProtocolMessage"/>-derived concrete class that this message can
+ /// deserialize to. Null if the request isn't recognized as a valid protocol message.
+ /// </returns>
+ public Type GetRequestMessageType(IDictionary<string, string> fields) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Analyzes an incoming request message payload to discover what kind of
+ /// message is embedded in it and returns the type, or null if no match is found.
+ /// </summary>
+ /// <param name="request">
+ /// The message that was sent as a request that resulted in the response.
+ /// </param>
+ /// <param name="fields">The name/value pairs that make up the message payload.</param>
+ /// <returns>
+ /// The <see cref="IProtocolMessage"/>-derived concrete class that this message can
+ /// deserialize to. Null if the request isn't recognized as a valid protocol message.
+ /// </returns>
+ public Type GetResponseMessageType(IProtocolMessage request, IDictionary<string, string> fields) {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+}
diff --git a/src/YOURLIBNAME/Properties/AssemblyInfo.cs b/src/DotNetOAuth/Properties/AssemblyInfo.cs index 14abe90..725caf8 100644 --- a/src/YOURLIBNAME/Properties/AssemblyInfo.cs +++ b/src/DotNetOAuth/Properties/AssemblyInfo.cs @@ -28,16 +28,16 @@ using System.Security; using System.Security.Permissions;
using System.Web.UI;
-[assembly: TagPrefix("YOURLIBNAME", "oauth")]
+[assembly: TagPrefix("DotNetOAuth", "oauth")]
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
-[assembly: AssemblyTitle("YOURLIBNAME")]
+[assembly: AssemblyTitle("DotNetOAuth")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("YOURLIBNAME")]
+[assembly: AssemblyProduct("DotNetOAuth")]
[assembly: AssemblyCopyright("Copyright © 2008")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -57,9 +57,9 @@ using System.Web.UI; // keep this assembly from being useful to shared host (medium trust) web sites.
[assembly: AllowPartiallyTrustedCallers]
-[assembly: InternalsVisibleTo("YOURLIBNAME.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")]
+[assembly: InternalsVisibleTo("DotNetOAuth.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")]
#else
-[assembly: InternalsVisibleTo("YOURLIBNAME.Test")]
+[assembly: InternalsVisibleTo("DotNetOAuth.Test")]
#endif
// Specify what permissions are required and optional for the assembly.
diff --git a/src/DotNetOAuth/Protocol.cs b/src/DotNetOAuth/Protocol.cs new file mode 100644 index 0000000..f3dc3b6 --- /dev/null +++ b/src/DotNetOAuth/Protocol.cs @@ -0,0 +1,75 @@ +//-----------------------------------------------------------------------
+// <copyright file="Protocol.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOAuth.Messaging;
+
+ /// <summary>
+ /// Constants used in the OAuth protocol.
+ /// </summary>
+ /// <remarks>
+ /// OAuth Protocol Parameter names and values are case sensitive. Each OAuth Protocol Parameters MUST NOT appear more than once per request, and are REQUIRED unless otherwise noted,
+ /// per OAuth 1.0 section 5.
+ /// </remarks>
+ internal class Protocol {
+ /// <summary>
+ /// The namespace to use for V1.0 of the protocol.
+ /// </summary>
+ internal const string DataContractNamespaceV10 = "http://oauth.net/core/1.0/";
+
+ /// <summary>
+ /// Gets the <see cref="Protocol"/> instance with values initialized for V1.0 of the protocol.
+ /// </summary>
+ internal static readonly Protocol V10 = new Protocol {
+ dataContractNamespace = DataContractNamespaceV10,
+ };
+
+ /// <summary>
+ /// The namespace to use for this version of the protocol.
+ /// </summary>
+ private string dataContractNamespace;
+
+ /// <summary>
+ /// The prefix used for all key names in the protocol.
+ /// </summary>
+ private string parameterPrefix = "oauth_";
+
+ /// <summary>
+ /// The scheme to use in Authorization header message requests.
+ /// </summary>
+ private string authorizationHeaderScheme = "OAuth";
+
+ /// <summary>
+ /// Gets the default <see cref="Protocol"/> instance.
+ /// </summary>
+ internal static Protocol Default { get { return V10; } }
+
+ /// <summary>
+ /// Gets the namespace to use for this version of the protocol.
+ /// </summary>
+ internal string DataContractNamespace {
+ get { return this.dataContractNamespace; }
+ }
+
+ /// <summary>
+ /// Gets the prefix used for all key names in the protocol.
+ /// </summary>
+ internal string ParameterPrefix {
+ get { return this.parameterPrefix; }
+ }
+
+ /// <summary>
+ /// Gets the scheme to use in Authorization header message requests.
+ /// </summary>
+ internal string AuthorizationHeaderScheme {
+ get { return this.authorizationHeaderScheme; }
+ }
+ }
+}
diff --git a/src/DotNetOAuth/ProtocolException.cs b/src/DotNetOAuth/ProtocolException.cs new file mode 100644 index 0000000..4505c59 --- /dev/null +++ b/src/DotNetOAuth/ProtocolException.cs @@ -0,0 +1,45 @@ +//-----------------------------------------------------------------------
+// <copyright file="ProtocolException.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth {
+ using System;
+
+ /// <summary>
+ /// An exception to represent errors in the local or remote implementation of the protocol.
+ /// </summary>
+ [Serializable]
+ public class ProtocolException : Exception {
+ /// <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.
+ /// </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) { }
+ }
+}
diff --git a/src/DotNetOAuth/ServiceProvider.cs b/src/DotNetOAuth/ServiceProvider.cs new file mode 100644 index 0000000..49bfb9f --- /dev/null +++ b/src/DotNetOAuth/ServiceProvider.cs @@ -0,0 +1,63 @@ +//-----------------------------------------------------------------------
+// <copyright file="ServiceProvider.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth {
+ using System;
+ using System.Collections.Generic;
+ using System.Text;
+ using System.Web;
+
+ /// <summary>
+ /// A web application that allows access via OAuth.
+ /// </summary>
+ /// <remarks>
+ /// <para>The Service Provider’s documentation should include:</para>
+ /// <list>
+ /// <item>The URLs (Request URLs) the Consumer will use when making OAuth requests, and the HTTP methods (i.e. GET, POST, etc.) used in the Request Token URL and Access Token URL.</item>
+ /// <item>Signature methods supported by the Service Provider.</item>
+ /// <item>Any additional request parameters that the Service Provider requires in order to obtain a Token. Service Provider specific parameters MUST NOT begin with oauth_.</item>
+ /// </list>
+ /// </remarks>
+ internal class ServiceProvider {
+ /// <summary>
+ /// The field used to store the value of the <see cref="RequestTokenUri"/> property.
+ /// </summary>
+ private Uri requestTokenUri;
+
+ /// <summary>
+ /// Gets or sets the URL used to obtain an unauthorized Request Token,
+ /// described in Section 6.1 (Obtaining an Unauthorized Request Token).
+ /// </summary>
+ /// <remarks>
+ /// The request URL query MUST NOT contain any OAuth Protocol Parameters.
+ /// </remarks>
+ /// <exception cref="ArgumentException">Thrown if this property is set to a URI with OAuth protocol parameters.</exception>
+ public Uri RequestTokenUri {
+ get {
+ return this.requestTokenUri;
+ }
+
+ set {
+ if (UriUtil.QueryStringContainsOAuthParameters(value)) {
+ throw new ArgumentException(Strings.RequestUrlMustNotHaveOAuthParameters);
+ }
+ this.requestTokenUri = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the URL used to obtain User authorization for Consumer access,
+ /// described in Section 6.2 (Obtaining User Authorization).
+ /// </summary>
+ public Uri UserAuthorizationUri { get; set; }
+
+ /// <summary>
+ /// Gets or sets the URL used to exchange the User-authorized Request Token
+ /// for an Access Token, described in Section 6.3 (Obtaining an Access Token).
+ /// </summary>
+ public Uri AccessTokenUri { get; set; }
+ }
+}
diff --git a/src/YOURLIBNAME/Settings.StyleCop b/src/DotNetOAuth/Settings.StyleCop index 2eaae96..2eaae96 100644 --- a/src/YOURLIBNAME/Settings.StyleCop +++ b/src/DotNetOAuth/Settings.StyleCop diff --git a/src/DotNetOAuth/Strings.Designer.cs b/src/DotNetOAuth/Strings.Designer.cs new file mode 100644 index 0000000..47a4c1c --- /dev/null +++ b/src/DotNetOAuth/Strings.Designer.cs @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.3053
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace DotNetOAuth {
+ 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 Strings {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Strings() {
+ }
+
+ /// <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("DotNetOAuth.Strings", typeof(Strings).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 HttpContext.Current is null. There must be an ASP.NET request in process for this operation to succeed..
+ /// </summary>
+ internal static string CurrentHttpContextRequired {
+ get {
+ return ResourceManager.GetString("CurrentHttpContextRequired", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to An invalid OAuth message received and discarded..
+ /// </summary>
+ internal static string InvalidIncomingMessage {
+ get {
+ return ResourceManager.GetString("InvalidIncomingMessage", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to The request URL query MUST NOT contain any OAuth Protocol Parameters..
+ /// </summary>
+ internal static string RequestUrlMustNotHaveOAuthParameters {
+ get {
+ return ResourceManager.GetString("RequestUrlMustNotHaveOAuthParameters", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/DotNetOAuth/Strings.resx b/src/DotNetOAuth/Strings.resx new file mode 100644 index 0000000..0e718d8 --- /dev/null +++ b/src/DotNetOAuth/Strings.resx @@ -0,0 +1,129 @@ +<?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="CurrentHttpContextRequired" xml:space="preserve">
+ <value>HttpContext.Current is null. There must be an ASP.NET request in process for this operation to succeed.</value>
+ </data>
+ <data name="InvalidIncomingMessage" xml:space="preserve">
+ <value>An invalid OAuth message received and discarded.</value>
+ </data>
+ <data name="RequestUrlMustNotHaveOAuthParameters" xml:space="preserve">
+ <value>The request URL query MUST NOT contain any OAuth Protocol Parameters.</value>
+ </data>
+</root>
\ No newline at end of file diff --git a/src/DotNetOAuth/UriUtil.cs b/src/DotNetOAuth/UriUtil.cs new file mode 100644 index 0000000..74f9e3e --- /dev/null +++ b/src/DotNetOAuth/UriUtil.cs @@ -0,0 +1,31 @@ +//-----------------------------------------------------------------------
+// <copyright file="UriUtil.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOAuth {
+ using System;
+ using System.Collections.Specialized;
+ using System.Linq;
+ using System.Web;
+
+ /// <summary>
+ /// Utility methods for working with URIs.
+ /// </summary>
+ internal class UriUtil {
+ /// <summary>
+ /// Tests a URI for the presence of an OAuth payload.
+ /// </summary>
+ /// <param name="uri">The URI to test.</param>
+ /// <returns>True if the URI contains an OAuth message.</returns>
+ internal static bool QueryStringContainsOAuthParameters(Uri uri) {
+ if (uri == null) {
+ return false;
+ }
+
+ NameValueCollection nvc = HttpUtility.ParseQueryString(uri.Query);
+ return nvc.Keys.OfType<string>().Any(key => key.StartsWith(Protocol.V10.ParameterPrefix, StringComparison.Ordinal));
+ }
+ }
+}
diff --git a/src/YOURLIBNAME/Util.cs b/src/DotNetOAuth/Util.cs index 16f52fa..e3af5f1 100644 --- a/src/YOURLIBNAME/Util.cs +++ b/src/DotNetOAuth/Util.cs @@ -3,7 +3,7 @@ // Copyright (c) Andrew Arnott. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
-namespace YOURLIBNAME {
+namespace DotNetOAuth {
using System.Globalization;
using System.Reflection;
diff --git a/src/LocalTestRun.testrunconfig b/src/LocalTestRun.testrunconfig index d03f62c..bad921b 100644 --- a/src/LocalTestRun.testrunconfig +++ b/src/LocalTestRun.testrunconfig @@ -3,7 +3,7 @@ <Description>This is a default test run configuration for a local test run.</Description>
<CodeCoverage enabled="true">
<Regular>
- <CodeCoverageItem binaryFile="C:\git\productname\bin\Debug\YOURLIBNAME.dll" pdbFile="C:\git\productname\bin\Debug\ProductName.pdb" instrumentInPlace="true" />
+ <CodeCoverageItem binaryFile="C:\git\dotnetoauth\bin\Debug\DotNetOAuth.dll" pdbFile="C:\git\dotnetoauth\bin\Debug\DotNetOAuth.pdb" instrumentInPlace="true" />
</Regular>
</CodeCoverage>
<TestTypeSpecific>
diff --git a/src/Settings.StyleCop b/src/Settings.StyleCop index 8831d6b..a00cce5 100644 --- a/src/Settings.StyleCop +++ b/src/Settings.StyleCop @@ -17,6 +17,31 @@ <BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
+ <Rule Name="ElementMustNotBeOnSingleLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="ClosingCurlyBracketMustBeFollowedByBlankLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ </Rules>
+ <AnalyzerSettings />
+ </Analyzer>
+ <Analyzer AnalyzerId="Microsoft.StyleCop.CSharp.ReadabilityRules">
+ <Rules>
+ <Rule Name="ParametersMustBeOnSameLineOrSeparateLines">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
+ <Rule Name="CodeMustNotContainMultipleStatementsOnOneLine">
+ <RuleSettings>
+ <BooleanProperty Name="Enabled">False</BooleanProperty>
+ </RuleSettings>
+ </Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
diff --git a/src/YOURLIBNAME.Test/Settings.StyleCop b/src/YOURLIBNAME.Test/Settings.StyleCop deleted file mode 100644 index 7f55ce6..0000000 --- a/src/YOURLIBNAME.Test/Settings.StyleCop +++ /dev/null @@ -1 +0,0 @@ -<StyleCopSettings Version="4.3" />
\ No newline at end of file |