diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2008-08-10 23:46:07 -0700 |
---|---|---|
committer | Andrew <andrewarnott@gmail.com> | 2008-09-01 11:02:38 -0700 |
commit | 5010504266d6e7fddfedf49ecbc572a2baba5e0a (patch) | |
tree | 01a40701524aa4d14b3187ff984c192736827ef8 | |
parent | 53c2d9b3b52608003a41f4195766f0c95bb0a25d (diff) | |
download | DotNetOpenAuth-5010504266d6e7fddfedf49ecbc572a2baba5e0a.zip DotNetOpenAuth-5010504266d6e7fddfedf49ecbc572a2baba5e0a.tar.gz DotNetOpenAuth-5010504266d6e7fddfedf49ecbc572a2baba5e0a.tar.bz2 |
Added some basic classes, message serialization and some unit tests.
-rw-r--r-- | src/DotNetOAuth.Test/DotNetOAuth.Test.csproj | 16 | ||||
-rw-r--r-- | src/DotNetOAuth.Test/MessageSerializerTest.cs | 68 | ||||
-rw-r--r-- | src/DotNetOAuth.Test/Mocks/TestMessage.cs | 13 | ||||
-rw-r--r-- | src/DotNetOAuth.Test/ServiceProviderTest.cs | 56 | ||||
-rw-r--r-- | src/DotNetOAuth/ClassDiagram.cd | 72 | ||||
-rw-r--r-- | src/DotNetOAuth/Consumer.cs | 11 | ||||
-rw-r--r-- | src/DotNetOAuth/DirectMessageChannel.cs | 11 | ||||
-rw-r--r-- | src/DotNetOAuth/DotNetOAuth.csproj | 28 | ||||
-rw-r--r-- | src/DotNetOAuth/IProtocolMessage.cs | 12 | ||||
-rw-r--r-- | src/DotNetOAuth/IndirectMessage.cs | 23 | ||||
-rw-r--r-- | src/DotNetOAuth/IndirectMessageEncoder.cs | 11 | ||||
-rw-r--r-- | src/DotNetOAuth/MessageScheme.cs | 24 | ||||
-rw-r--r-- | src/DotNetOAuth/Protocol.cs | 32 | ||||
-rw-r--r-- | src/DotNetOAuth/ProtocolMessageSerializer.cs | 346 | ||||
-rw-r--r-- | src/DotNetOAuth/ServiceProvider.cs | 48 | ||||
-rw-r--r-- | src/DotNetOAuth/Strings.Designer.cs | 72 | ||||
-rw-r--r-- | src/DotNetOAuth/Strings.resx | 123 | ||||
-rw-r--r-- | src/DotNetOAuth/UriUtil.cs | 14 | ||||
-rw-r--r-- | src/LocalTestRun.testrunconfig | 21 |
19 files changed, 999 insertions, 2 deletions
diff --git a/src/DotNetOAuth.Test/DotNetOAuth.Test.csproj b/src/DotNetOAuth.Test/DotNetOAuth.Test.csproj index 6fc049f..d136b2c 100644 --- a/src/DotNetOAuth.Test/DotNetOAuth.Test.csproj +++ b/src/DotNetOAuth.Test/DotNetOAuth.Test.csproj @@ -44,9 +44,25 @@ <Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Data.DataSetExtensions">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Runtime.Serialization">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Web" />
+ <Reference Include="System.Xml" />
+ <Reference Include="System.Xml.Linq">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
</ItemGroup>
<ItemGroup>
+ <Compile Include="MessageSerializerTest.cs" />
+ <Compile Include="Mocks\TestMessage.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="ServiceProviderTest.cs" />
+ <Compile Include="TestBase.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DotNetOAuth\DotNetOAuth.csproj">
diff --git a/src/DotNetOAuth.Test/MessageSerializerTest.cs b/src/DotNetOAuth.Test/MessageSerializerTest.cs new file mode 100644 index 0000000..2db8343 --- /dev/null +++ b/src/DotNetOAuth.Test/MessageSerializerTest.cs @@ -0,0 +1,68 @@ +using System;
+using System.Collections.Generic;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace DotNetOAuth.Test {
+ [TestClass()]
+ public class MessageSerializerTest : TestBase {
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void SerializeNull() {
+ var serializer = new ProtocolMessageSerializer<Mocks.TestMessage>();
+ serializer.Serialize(null);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void DeserializeNull() {
+ var serializer = new ProtocolMessageSerializer<Mocks.TestMessage>();
+ serializer.Deserialize(null);
+ }
+
+ /// <summary>
+ /// A test for Deserialize
+ /// </summary>
+ [TestMethod()]
+ public void DeserializeSimple() {
+ var serializer = new ProtocolMessageSerializer<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 = serializer.Deserialize(fields);
+ Assert.AreEqual(15, actual.Age);
+ Assert.AreEqual("Andrew", actual.Name);
+ Assert.AreEqual(null, actual.EmptyMember);
+ }
+
+ /// <summary>
+ /// A test for Deserialize
+ /// </summary>
+ [TestMethod()]
+ public void DeserializeEmpty() {
+ var serializer = new ProtocolMessageSerializer<Mocks.TestMessage>();
+ Dictionary<string, string> fields = new Dictionary<string, string>(StringComparer.Ordinal);
+ var actual = serializer.Deserialize(fields);
+ Assert.AreEqual(0, actual.Age);
+ }
+
+ /// <summary>
+ /// A test for Serialize
+ /// </summary>
+ [TestMethod()]
+ public void SerializeTest() {
+ var serializer = new ProtocolMessageSerializer<Mocks.TestMessage>();
+ var message = new Mocks.TestMessage { Age = 15, Name = "Andrew" };
+ IDictionary<string, string> actual = serializer.Serialize(message);
+ Assert.AreEqual(2, 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.IsFalse(actual.ContainsKey("EmptyMember"));
+ }
+ }
+}
diff --git a/src/DotNetOAuth.Test/Mocks/TestMessage.cs b/src/DotNetOAuth.Test/Mocks/TestMessage.cs new file mode 100644 index 0000000..1a8eba7 --- /dev/null +++ b/src/DotNetOAuth.Test/Mocks/TestMessage.cs @@ -0,0 +1,13 @@ +using System.Runtime.Serialization;
+
+namespace DotNetOAuth.Test.Mocks {
+ [DataContract(Namespace = Protocol.DataContractNamespace)]
+ class TestMessage : IProtocolMessage {
+ [DataMember(Name = "age")]
+ public int Age { get; set; }
+ [DataMember]
+ public string Name { get; set; }
+ [DataMember]
+ public string EmptyMember { get; set; }
+ }
+}
diff --git a/src/DotNetOAuth.Test/ServiceProviderTest.cs b/src/DotNetOAuth.Test/ServiceProviderTest.cs new file mode 100644 index 0000000..56ef619 --- /dev/null +++ b/src/DotNetOAuth.Test/ServiceProviderTest.cs @@ -0,0 +1,56 @@ +using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace DotNetOAuth.Test {
+ [TestClass()]
+ public class ServiceProviderTest {
+ /// <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/ClassDiagram.cd b/src/DotNetOAuth/ClassDiagram.cd new file mode 100644 index 0000000..4d68730 --- /dev/null +++ b/src/DotNetOAuth/ClassDiagram.cd @@ -0,0 +1,72 @@ +<?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.5" />
+ <TypeIdentifier>
+ <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
+ <FileName>Consumer.cs</FileName>
+ </TypeIdentifier>
+ </Class>
+ <Class Name="DotNetOAuth.DirectMessageChannel">
+ <Position X="5.75" Y="2" Width="2" />
+ <TypeIdentifier>
+ <HashCode>AAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
+ <FileName>DirectMessageChannel.cs</FileName>
+ </TypeIdentifier>
+ </Class>
+ <Class Name="DotNetOAuth.IndirectMessageEncoder">
+ <Position X="5.75" Y="3.25" Width="2" />
+ <TypeIdentifier>
+ <HashCode>AAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
+ <FileName>IndirectMessageEncoder.cs</FileName>
+ </TypeIdentifier>
+ </Class>
+ <Class Name="DotNetOAuth.IndirectMessage" Collapsed="true">
+ <Position X="5.75" Y="4.5" Width="2" />
+ <TypeIdentifier>
+ <HashCode>AAAAAAAEAABAAAAAAEABAAAACAAAAAAAAAAAAAAAAAA=</HashCode>
+ <FileName>IndirectMessage.cs</FileName>
+ </TypeIdentifier>
+ </Class>
+ <Class Name="DotNetOAuth.ProtocolMessageSerializer<T>">
+ <Position X="6" Y="0.5" Width="1.5" />
+ <NestedTypes>
+ <Class Name="DotNetOAuth.ProtocolMessageSerializer<T>.PseudoXmlWriter" Collapsed="true">
+ <TypeIdentifier>
+ <NewMemberFileName>ProtocolMessageSerializer.cs</NewMemberFileName>
+ </TypeIdentifier>
+ </Class>
+ <Class Name="DotNetOAuth.ProtocolMessageSerializer<T>.PseudoXmlReader" Collapsed="true">
+ <TypeIdentifier>
+ <NewMemberFileName>ProtocolMessageSerializer.cs</NewMemberFileName>
+ </TypeIdentifier>
+ </Class>
+ </NestedTypes>
+ <TypeIdentifier>
+ <HashCode>AAAAAAAAIAAAAAAAAAAAAAAAACBAAAAAAAAAAAAAAAA=</HashCode>
+ <FileName>ProtocolMessageSerializer.cs</FileName>
+ </TypeIdentifier>
+ </Class>
+ <Interface Name="DotNetOAuth.IProtocolMessage">
+ <Position X="2.75" Y="1.5" Width="1.5" />
+ <TypeIdentifier>
+ <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
+ <FileName>IProtocolMessage.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>
+ <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..66a6b05 --- /dev/null +++ b/src/DotNetOAuth/Consumer.cs @@ -0,0 +1,11 @@ +using System;
+using System.Collections.Generic;
+using System.Text;
+
+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/DotNetOAuth/DirectMessageChannel.cs b/src/DotNetOAuth/DirectMessageChannel.cs new file mode 100644 index 0000000..72b45d8 --- /dev/null +++ b/src/DotNetOAuth/DirectMessageChannel.cs @@ -0,0 +1,11 @@ +using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace DotNetOAuth {
+ internal class DirectMessageChannel {
+ public void Send(IProtocolMessage message) {
+ throw new System.NotImplementedException();
+ }
+ }
+}
diff --git a/src/DotNetOAuth/DotNetOAuth.csproj b/src/DotNetOAuth/DotNetOAuth.csproj index f4cd184..06a6227 100644 --- a/src/DotNetOAuth/DotNetOAuth.csproj +++ b/src/DotNetOAuth/DotNetOAuth.csproj @@ -52,6 +52,9 @@ <Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
+ <Reference Include="System.Runtime.Serialization">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
@@ -63,8 +66,33 @@ <Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="Consumer.cs" />
+ <Compile Include="DirectMessageChannel.cs" />
+ <Compile Include="IndirectMessage.cs" />
+ <Compile Include="IndirectMessageEncoder.cs" />
+ <Compile Include="IProtocolMessage.cs" />
+ <Compile Include="MessageScheme.cs" />
+ <Compile Include="ProtocolMessageSerializer.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="Strings.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>Strings.Designer.cs</LastGenOutput>
+ <SubType>Designer</SubType>
+ </EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\tools\DotNetOAuth.Versioning.targets" />
diff --git a/src/DotNetOAuth/IProtocolMessage.cs b/src/DotNetOAuth/IProtocolMessage.cs new file mode 100644 index 0000000..ccf4e5a --- /dev/null +++ b/src/DotNetOAuth/IProtocolMessage.cs @@ -0,0 +1,12 @@ +using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace DotNetOAuth {
+ /// <summary>
+ /// The interface that classes must implement to be serialized/deserialized
+ /// as OAuth messages.
+ /// </summary>
+ interface IProtocolMessage {
+ }
+}
diff --git a/src/DotNetOAuth/IndirectMessage.cs b/src/DotNetOAuth/IndirectMessage.cs new file mode 100644 index 0000000..114bdc7 --- /dev/null +++ b/src/DotNetOAuth/IndirectMessage.cs @@ -0,0 +1,23 @@ +using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Net;
+
+namespace DotNetOAuth {
+ public class IndirectMessage {
+ public WebHeaderCollection Headers { get; set; }
+
+ public byte[] Body { get; set; }
+
+ public HttpStatusCode Status { get; set; }
+
+ internal IProtocolMessage OriginalMessage { get; set; }
+
+ /// <summary>
+ /// Requires a current HttpContext.
+ /// </summary>
+ public void Send() {
+ throw new System.NotImplementedException();
+ }
+ }
+}
diff --git a/src/DotNetOAuth/IndirectMessageEncoder.cs b/src/DotNetOAuth/IndirectMessageEncoder.cs new file mode 100644 index 0000000..53caef1 --- /dev/null +++ b/src/DotNetOAuth/IndirectMessageEncoder.cs @@ -0,0 +1,11 @@ +using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace DotNetOAuth {
+ internal class IndirectMessageEncoder {
+ public IndirectMessage Encode(IProtocolMessage message) {
+ throw new System.NotImplementedException();
+ }
+ }
+}
diff --git a/src/DotNetOAuth/MessageScheme.cs b/src/DotNetOAuth/MessageScheme.cs new file mode 100644 index 0000000..8878f40 --- /dev/null +++ b/src/DotNetOAuth/MessageScheme.cs @@ -0,0 +1,24 @@ +using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace DotNetOAuth {
+ 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/Protocol.cs b/src/DotNetOAuth/Protocol.cs new file mode 100644 index 0000000..0969a26 --- /dev/null +++ b/src/DotNetOAuth/Protocol.cs @@ -0,0 +1,32 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace DotNetOAuth {
+ /// <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>
+ class Protocol {
+ internal static readonly Protocol Default = V10;
+ internal static readonly Protocol V10 = new Protocol {
+ };
+
+ internal const string DataContractNamespace = "http://oauth.net/core/1.0/";
+ internal string ParameterPrefix = "oauth_";
+
+ /// <summary>
+ /// Strings that identify the various message schemes.
+ /// </summary>
+ /// <remarks>
+ /// These strings should be checked with case INsensitivity.
+ /// </remarks>
+ internal Dictionary<MessageScheme, string> MessageSchemes = new Dictionary<MessageScheme,string> {
+ { MessageScheme.AuthorizationHeaderRequest, "OAuth" },
+ };
+ }
+}
diff --git a/src/DotNetOAuth/ProtocolMessageSerializer.cs b/src/DotNetOAuth/ProtocolMessageSerializer.cs new file mode 100644 index 0000000..da564d2 --- /dev/null +++ b/src/DotNetOAuth/ProtocolMessageSerializer.cs @@ -0,0 +1,346 @@ +using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Xml;
+
+namespace DotNetOAuth {
+ /// <summary>
+ /// Serializes/deserializes OAuth messages for/from transit.
+ /// </summary>
+ internal class ProtocolMessageSerializer<T> where T : IProtocolMessage {
+ DataContractSerializer serializer;
+ const string rootName = "root";
+
+ internal ProtocolMessageSerializer() {
+ serializer = new DataContractSerializer(typeof(T), rootName, Protocol.DataContractNamespace);
+ }
+
+ /// <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>
+ internal IDictionary<string, string> Serialize(T message) {
+ if (message == null) throw new ArgumentNullException("message");
+
+ var fields = new Dictionary<string, string>();
+ XmlWriter writer = new ProtocolMessageSerializer<T>.PseudoXmlWriter(fields);
+ serializer.WriteObjectContent(writer, message);
+ writer.Close();
+ return fields;
+ }
+
+ /// <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>
+ internal T Deserialize(IDictionary<string, string> fields) {
+ if (fields == null) throw new ArgumentNullException("fields");
+
+ var reader = new PseudoXmlReader(fields);
+ //var reader = new XmlTextReader(new StringReader("<root xmlns='http://oauth.net/core/1.0/'><Name>Andrew</Name><age>15</age></root>"));
+ T result = (T)serializer.ReadObject(reader, false);
+ return result;
+ }
+
+ /// <summary>
+ /// Writes out a dictionary as if it were XML.
+ /// </summary>
+ class PseudoXmlWriter : XmlWriter {
+ Dictionary<string, string> dictionary;
+ string key;
+ StringBuilder value = new StringBuilder();
+
+ internal PseudoXmlWriter(Dictionary<string, string> dictionary) {
+ this.dictionary = dictionary;
+ }
+
+ public override void WriteStartElement(string prefix, string localName, string ns) {
+ key = localName;
+ value.Length = 0;
+ }
+
+ public override void WriteString(string text) {
+ if (!string.IsNullOrEmpty(key)) {
+ value.Append(text);
+ }
+ }
+
+ public override void WriteEndElement() {
+ if (key != null) {
+ dictionary[key] = value.ToString();
+ key = null;
+ value.Length = 0;
+ }
+ }
+
+ public override WriteState WriteState {
+ get { return WriteState.Element; }
+ }
+
+ public override void WriteStartAttribute(string prefix, string localName, string ns) {
+ key = null;
+ }
+
+ public override void WriteEndAttribute() { }
+
+ public override void Close() { }
+
+ #region Unimplemented methods
+
+ public override void Flush() {
+ throw new NotImplementedException();
+ }
+
+ public override string LookupPrefix(string ns) {
+ throw new NotImplementedException();
+ }
+
+ public override void WriteBase64(byte[] buffer, int index, int count) {
+ throw new NotImplementedException();
+ }
+
+ public override void WriteCData(string text) {
+ throw new NotImplementedException();
+ }
+
+ public override void WriteCharEntity(char ch) {
+ throw new NotImplementedException();
+ }
+
+ public override void WriteChars(char[] buffer, int index, int count) {
+ throw new NotImplementedException();
+ }
+
+ public override void WriteComment(string text) {
+ throw new NotImplementedException();
+ }
+
+ public override void WriteDocType(string name, string pubid, string sysid, string subset) {
+ throw new NotImplementedException();
+ }
+
+ public override void WriteEndDocument() {
+ throw new NotImplementedException();
+ }
+
+ public override void WriteEntityRef(string name) {
+ throw new NotImplementedException();
+ }
+
+ public override void WriteFullEndElement() {
+ throw new NotImplementedException();
+ }
+
+ public override void WriteProcessingInstruction(string name, string text) {
+ throw new NotImplementedException();
+ }
+
+ public override void WriteRaw(string data) {
+ throw new NotImplementedException();
+ }
+
+ public override void WriteRaw(char[] buffer, int index, int count) {
+ throw new NotImplementedException();
+ }
+
+ public override void WriteStartDocument(bool standalone) {
+ throw new NotImplementedException();
+ }
+
+ public override void WriteStartDocument() {
+ throw new NotImplementedException();
+ }
+
+ public override void WriteSurrogateCharEntity(char lowChar, char highChar) {
+ throw new NotImplementedException();
+ }
+
+ public override void WriteWhitespace(string ws) {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+
+ /// <summary>
+ /// Reads in a dictionary as if it were XML.
+ /// </summary>
+ class PseudoXmlReader : XmlReader {
+ IDictionary<string, string> dictionary;
+ IEnumerator<string> keyEnumerator;
+ bool isFinished;
+ int depth;
+ XmlNodeType currentNode = XmlNodeType.None;
+
+ internal PseudoXmlReader(IDictionary<string, string> dictionary) {
+ this.dictionary = dictionary;
+ keyEnumerator = dictionary.Keys.OrderBy(key => key, StringComparer.Ordinal).GetEnumerator();
+ }
+
+ public override int AttributeCount {
+ get { return 0; }
+ }
+
+ public override bool IsEmptyElement {
+ get {
+ if (keyEnumerator.Current == null) {
+ return isFinished;
+ }
+ string value;
+ bool result = !dictionary.TryGetValue(keyEnumerator.Current, out value) || String.IsNullOrEmpty(value);
+ return result;
+ }
+ }
+
+ public override string LocalName {
+ get { return keyEnumerator.Current; }
+ }
+
+ public override bool MoveToElement() {
+ bool result = currentNode != XmlNodeType.Element && depth > 0;
+ currentNode = depth > 0 ? XmlNodeType.Element : XmlNodeType.None;
+ return result;
+ }
+
+ public override bool MoveToNextAttribute() {
+ if (depth == 1 && currentNode != XmlNodeType.Attribute) {
+ currentNode = XmlNodeType.Attribute;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public override string NamespaceURI {
+ get {
+ string result = depth == 1 && currentNode == XmlNodeType.Attribute ?
+ "http://www.w3.org/2000/xmlns/" : Protocol.DataContractNamespace;
+ return result;
+ }
+ }
+
+ public override XmlNodeType NodeType {
+ get { return currentNode; }
+ }
+
+ public override bool Read() {
+ if (isFinished) {
+ if (depth > 0) {
+ depth--;
+ }
+ return depth > 0;
+ }
+ switch (depth) {
+ case 0: // moving to root node
+ depth++; // -> 1
+ currentNode = XmlNodeType.Element;
+ return true;
+ case 1: // moving to first content node
+ depth++; // -> 2
+ isFinished = !keyEnumerator.MoveNext();
+ currentNode = isFinished ? XmlNodeType.EndElement : XmlNodeType.Element;
+ return true;
+ case 2: // content node
+ switch (currentNode) {
+ case XmlNodeType.Element:
+ currentNode = XmlNodeType.Text;
+ return true;
+ case XmlNodeType.Text:
+ currentNode = XmlNodeType.EndElement;
+ return true;
+ case XmlNodeType.EndElement:
+ bool result = keyEnumerator.MoveNext();
+ if (!result) {
+ isFinished = true;
+ depth--;
+ currentNode = XmlNodeType.EndElement;
+ } else {
+ currentNode = XmlNodeType.Element;
+ }
+ return true;
+ }
+ break;
+ }
+ throw new InvalidOperationException();
+ }
+
+ public override string Value {
+ get { return dictionary[keyEnumerator.Current]; }
+ }
+
+ #region Unimplemented methods
+
+ public override string BaseURI {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override void Close() {
+ throw new NotImplementedException();
+ }
+
+ public override int Depth {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override bool EOF {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override string GetAttribute(int i) {
+ throw new NotImplementedException();
+ }
+
+ public override string GetAttribute(string name, string namespaceURI) {
+ throw new NotImplementedException();
+ }
+
+ public override string GetAttribute(string name) {
+ throw new NotImplementedException();
+ }
+
+ public override bool HasValue {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override string LookupNamespace(string prefix) {
+ throw new NotImplementedException();
+ }
+
+ public override bool MoveToAttribute(string name, string ns) {
+ throw new NotImplementedException();
+ }
+
+ public override bool MoveToAttribute(string name) {
+ throw new NotImplementedException();
+ }
+
+ public override bool MoveToFirstAttribute() {
+ throw new NotImplementedException();
+ }
+
+ public override XmlNameTable NameTable {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override string Prefix {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override ReadState ReadState {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override bool ReadAttributeValue() {
+ throw new NotImplementedException();
+ }
+
+ public override void ResolveEntity() {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+ }
+}
diff --git a/src/DotNetOAuth/ServiceProvider.cs b/src/DotNetOAuth/ServiceProvider.cs new file mode 100644 index 0000000..345c5cb --- /dev/null +++ b/src/DotNetOAuth/ServiceProvider.cs @@ -0,0 +1,48 @@ +using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Web;
+
+namespace DotNetOAuth {
+ /// <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 {
+ private Uri requestTokenUri;
+
+ /// <summary>
+ /// 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 requestTokenUri; }
+ set {
+ if (UriUtil.QueryStringContainsOAuthParameters(value)) {
+ throw new ArgumentException(Strings.RequestUrlMustNotHaveOAuthParameters);
+ }
+ requestTokenUri = value;
+ }
+ }
+
+ /// <summary>
+ /// 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>
+ /// 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/DotNetOAuth/Strings.Designer.cs b/src/DotNetOAuth/Strings.Designer.cs new file mode 100644 index 0000000..4679c2c --- /dev/null +++ b/src/DotNetOAuth/Strings.Designer.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.1434
+//
+// 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 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..52b5577 --- /dev/null +++ b/src/DotNetOAuth/Strings.resx @@ -0,0 +1,123 @@ +<?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="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..9ce913d --- /dev/null +++ b/src/DotNetOAuth/UriUtil.cs @@ -0,0 +1,14 @@ +using System;
+using System.Collections.Specialized;
+using System.Linq;
+using System.Web;
+
+namespace DotNetOAuth {
+ class UriUtil {
+ 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/LocalTestRun.testrunconfig b/src/LocalTestRun.testrunconfig index a524b83..bad921b 100644 --- a/src/LocalTestRun.testrunconfig +++ b/src/LocalTestRun.testrunconfig @@ -1,5 +1,22 @@ <?xml version="1.0" encoding="UTF-8"?>
<TestRunConfiguration name="Local Test Run" id="abbd81c0-7d7b-4c98-878c-05dbead62c27" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2006">
<Description>This is a default test run configuration for a local test run.</Description>
- <TestTypeSpecific />
-</TestRunConfiguration> + <CodeCoverage enabled="true">
+ <Regular>
+ <CodeCoverageItem binaryFile="C:\git\dotnetoauth\bin\Debug\DotNetOAuth.dll" pdbFile="C:\git\dotnetoauth\bin\Debug\DotNetOAuth.pdb" instrumentInPlace="true" />
+ </Regular>
+ </CodeCoverage>
+ <TestTypeSpecific>
+ <WebTestRunConfiguration testTypeId="4e7599fa-5ecb-43e9-a887-cd63cf72d207">
+ <Browser name="Internet Explorer 7.0">
+ <Headers>
+ <Header name="User-Agent" value="Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)" />
+ <Header name="Accept" value="*/*" />
+ <Header name="Accept-Language" value="{{$IEAcceptLanguage}}" />
+ <Header name="Accept-Encoding" value="GZIP" />
+ </Headers>
+ </Browser>
+ <Network Name="LAN" BandwidthInKbps="0" />
+ </WebTestRunConfiguration>
+ </TestTypeSpecific>
+</TestRunConfiguration>
\ No newline at end of file |