summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs77
-rw-r--r--src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs9
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs27
3 files changed, 99 insertions, 14 deletions
diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs
index 401153d..d660748 100644
--- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs
+++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs
@@ -15,6 +15,7 @@ namespace DotNetOpenAuth.Test.ChannelElements {
using System.Xml;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.Messaging.Reflection;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.Test.Mocks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -69,6 +70,41 @@ namespace DotNetOpenAuth.Test.ChannelElements {
this.ParameterizedReceiveTest(HttpDeliveryMethods.AuthorizationHeaderRequest);
}
+ /// <summary>
+ /// Verifies that the OAuth ReadFromRequest method gathers parameters
+ /// from the Authorization header, the query string and the entity form data.
+ /// </summary>
+ [TestMethod]
+ public void ReadFromRequestAuthorizationScattered() {
+ // Start by creating a standard POST HTTP request.
+ var fields = new Dictionary<string, string> {
+ { "age", "15" },
+ };
+ HttpRequestInfo requestInfo = CreateHttpRequestInfo(HttpDeliveryMethods.PostRequest, fields);
+
+ // Now add another field to the request URL
+ UriBuilder builder = new UriBuilder(requestInfo.Url);
+ builder.Query = "Name=Andrew";
+ requestInfo.Url = builder.Uri;
+ requestInfo.RawUrl = builder.Path + builder.Query + builder.Fragment;
+
+ // Finally, add an Authorization header
+ fields = new Dictionary<string, string> {
+ { "Location", "http://hostb/pathB" },
+ { "Timestamp", XmlConvert.ToString(DateTime.UtcNow, XmlDateTimeSerializationMode.Utc) },
+ };
+ requestInfo.Headers.Add(HttpRequestHeader.Authorization, CreateAuthorizationHeader(fields));
+
+ IDirectedProtocolMessage requestMessage = this.channel.ReadFromRequest(requestInfo);
+
+ 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);
+ }
+
[TestMethod]
public void ReadFromRequestForm() {
this.ParameterizedReceiveTest(HttpDeliveryMethods.PostRequest);
@@ -144,6 +180,43 @@ namespace DotNetOpenAuth.Test.ChannelElements {
this.ParameterizedRequestTest(HttpDeliveryMethods.AuthorizationHeaderRequest);
}
+ /// <summary>
+ /// Verifies that message parts can be distributed to the query, form, and Authorization header.
+ /// </summary>
+ [TestMethod]
+ public void RequestUsingAuthorizationHeaderScattered() {
+ TestDirectedMessage request = new TestDirectedMessage(MessageTransport.Direct) {
+ Age = 15,
+ Name = "Andrew",
+ Location = new Uri("http://hostb/pathB"),
+ Recipient = new Uri("http://localtest"),
+ Timestamp = DateTime.UtcNow,
+ HttpMethods = HttpDeliveryMethods.AuthorizationHeaderRequest,
+ };
+
+
+ // ExtraData should appear in the form since this is a POST request,
+ // and only official message parts get a place in the Authorization header.
+ ((IProtocolMessage)request).ExtraData["appearinform"] = "formish";
+ request.Recipient = new Uri("http://localhost/?appearinquery=queryish");
+ request.HttpMethods = HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.PostRequest;
+
+ HttpWebRequest webRequest = this.channel.InitializeRequest(request);
+ Assert.IsNotNull(webRequest);
+ Assert.AreEqual("POST", webRequest.Method);
+ Assert.AreEqual(request.Recipient, webRequest.RequestUri);
+
+ var declaredParts = new Dictionary<string, string> {
+ { "age", request.Age.ToString() },
+ { "Name", request.Name },
+ { "Location", request.Location.AbsoluteUri },
+ { "Timestamp", XmlConvert.ToString(request.Timestamp, XmlDateTimeSerializationMode.Utc) },
+ };
+
+ Assert.AreEqual(CreateAuthorizationHeader(declaredParts), webRequest.Headers[HttpRequestHeader.Authorization]);
+ Assert.AreEqual("appearinform=formish", this.webRequestHandler.RequestEntityAsString);
+ }
+
[TestMethod]
public void RequestUsingGet() {
this.ParameterizedRequestTest(HttpDeliveryMethods.GetRequest);
@@ -171,9 +244,7 @@ namespace DotNetOpenAuth.Test.ChannelElements {
}
private static string CreateAuthorizationHeader(IDictionary<string, string> fields) {
- if (fields == null) {
- throw new ArgumentNullException("fields");
- }
+ ErrorUtilities.VerifyArgumentNotNull(fields, "fields");
StringBuilder authorization = new StringBuilder();
authorization.Append("OAuth ");
diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs
index cff46af..93c0b3f 100644
--- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs
+++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs
@@ -50,6 +50,15 @@ namespace DotNetOpenAuth.Test.ChannelElements {
Assert.AreEqual(
"GET&https%3A%2F%2Fwww.google.com%2Faccounts%2FOAuthGetRequestToken&oauth_consumer_key%3Dnerdbank.org%26oauth_nonce%3Dfe4045a3f0efdd1e019fa8f8ae3f5c38%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1222665749%26oauth_version%3D1.0%26scope%3Dhttp%253A%252F%252Fwww.google.com%252Fm8%252Ffeeds%252F",
SigningBindingElementBase_Accessor.ConstructSignatureBaseString(message, MessageDictionary_Accessor.AttachShadow(this.MessageDescriptions.GetAccessor(message))));
+
+ // This is a simulation of receiving the message, where the query string is still in the URL,
+ // but has been read into ExtraData, so parameters in the query string appear twice.
+ message = CreateTestRequestTokenMessage(
+ this.MessageDescriptions,
+ new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken?scope=http://www.google.com/m8/feeds/", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.PostRequest));
+ Assert.AreEqual(
+ "GET&https%3A%2F%2Fwww.google.com%2Faccounts%2FOAuthGetRequestToken&oauth_consumer_key%3Dnerdbank.org%26oauth_nonce%3Dfe4045a3f0efdd1e019fa8f8ae3f5c38%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1222665749%26oauth_version%3D1.0%26scope%3Dhttp%253A%252F%252Fwww.google.com%252Fm8%252Ffeeds%252F",
+ SigningBindingElementBase_Accessor.ConstructSignatureBaseString(message, MessageDictionary_Accessor.AttachShadow(this.MessageDescriptions.GetAccessor(message))));
}
internal static UnauthorizedTokenRequest CreateTestRequestTokenMessage(MessageDescriptionCollection messageDescriptions, MessageReceivingEndpoint endpoint) {
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs
index d1fc10b..9910497 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs
@@ -167,22 +167,27 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
signatureBaseStringElements.Add(message.HttpMethod.ToUpperInvariant());
var encodedDictionary = OAuthChannel.GetUriEscapedParameters(messageDictionary);
- encodedDictionary.Remove("oauth_signature");
+
+ // An incoming message will already have included the query and form parameters
+ // in the message dictionary, but an outgoing message COULD have SOME parameters
+ // in the query that are not in the message dictionary because they were included
+ // in the receiving endpoint (the original URL).
+ // In an outgoing message, the POST entity can only contain parameters if they were
+ // in the message dictionary, so no need to pull out any parameters from there.
if (message.Recipient.Query != null) {
- // It seeems to me a deviation from the OAuth 1.0 spec to be willing to scrape the query
- // for parameters on anything but GET requests, but Google does it so to interop we must
- // as well. Besides, it seems more secure to sign everything if it's there.
NameValueCollection nvc = HttpUtility.ParseQueryString(message.Recipient.Query);
foreach (string key in nvc) {
- encodedDictionary.Add(Uri.EscapeDataString(key), Uri.EscapeDataString(nvc[key]));
+ string escapedKey = Uri.EscapeDataString(key);
+ string escapedValue = Uri.EscapeDataString(nvc[key]);
+ string existingValue;
+ if (!encodedDictionary.TryGetValue(escapedKey, out existingValue)) {
+ encodedDictionary.Add(escapedKey, escapedValue);
+ } else {
+ ErrorUtilities.VerifyInternal(escapedValue == existingValue, "Somehow we have conflicting values for the '{0}' parameter.", escapedKey);
+ }
}
- } else if (message.HttpMethod == "POST") {
- // If the HttpWebRequest that we're sending out has a content-type header
- // of application/x-www-form-urlencoded, we should be parsing out those parameters
- // and adding them to this dictionary as well.
- // But at this point we don't have access to the HttpWebRequest (design flaw?)
- // TODO: figure this out.
}
+ encodedDictionary.Remove("oauth_signature");
UriBuilder endpoint = new UriBuilder(message.Recipient);
endpoint.Query = null;