diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2010-01-11 22:18:22 -0800 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2010-01-11 22:18:22 -0800 |
commit | 3999579271b79a1c4443506028e4df4012f1519d (patch) | |
tree | fa6488b270096ed3d64667fe6383b0af80c19cc2 /src | |
parent | 7bda163a8a8d5a82e2f29833c8762aa8852bd3d4 (diff) | |
download | DotNetOpenAuth-3999579271b79a1c4443506028e4df4012f1519d.zip DotNetOpenAuth-3999579271b79a1c4443506028e4df4012f1519d.tar.gz DotNetOpenAuth-3999579271b79a1c4443506028e4df4012f1519d.tar.bz2 |
Fixed some bugs in the way multipart messages were handled or even allowed in OAuth.
Diffstat (limited to 'src')
6 files changed, 51 insertions, 29 deletions
diff --git a/src/DotNetOpenAuth/Messaging/Channel.cs b/src/DotNetOpenAuth/Messaging/Channel.cs index f3ac6e7..8d43322 100644 --- a/src/DotNetOpenAuth/Messaging/Channel.cs +++ b/src/DotNetOpenAuth/Messaging/Channel.cs @@ -841,10 +841,10 @@ namespace DotNetOpenAuth.Messaging { var requestMessageWithBinaryData = requestMessage as IMessageWithBinaryData; if (requestMessageWithBinaryData != null && requestMessageWithBinaryData.SendAsMultipart) { + var multiPartFields = new List<MultipartPostPart>(requestMessageWithBinaryData.BinaryData); + // When sending multi-part, all data gets send as multi-part -- even the non-binary data. - var multiPartFields = new List<MultipartPostPart>( - fields.Select(field => MultipartPostPart.CreateFormPart(field.Key, field.Value))); - multiPartFields.AddRange(requestMessageWithBinaryData.BinaryData); + multiPartFields.AddRange(fields.Select(field => MultipartPostPart.CreateFormPart(field.Key, field.Value))); this.SendParametersInEntityAsMultiPart(httpRequest, multiPartFields); } else { ErrorUtilities.VerifyProtocol(requestMessageWithBinaryData == null || requestMessageWithBinaryData.BinaryData.Count == 0, MessagingStrings.BinaryDataRequiresMultipart); diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs index 5013a08..1af4e1c 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs @@ -88,7 +88,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// </summary> /// <param name="message">The message with data to encode.</param> /// <returns>A dictionary of name-value pairs with their strings encoded.</returns> - internal static IDictionary<string, string> GetUriEscapedParameters(MessageDictionary message) { + internal static IDictionary<string, string> GetUriEscapedParameters(IEnumerable<KeyValuePair<string, string>> message) { var encodedDictionary = new Dictionary<string, string>(); UriEscapeParameters(message, encodedDictionary); return encodedDictionary; @@ -195,6 +195,8 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { if ((transmissionMethod & HttpDeliveryMethods.AuthorizationHeaderRequest) != 0) { httpRequest = this.InitializeRequestAsAuthHeader(request); } else if ((transmissionMethod & HttpDeliveryMethods.PostRequest) != 0) { + var requestMessageWithBinaryData = request as IMessageWithBinaryData; + ErrorUtilities.VerifyProtocol(requestMessageWithBinaryData == null || !requestMessageWithBinaryData.SendAsMultipart, OAuthStrings.MultipartPostMustBeUsedWithAuthHeader); httpRequest = this.InitializeRequestAsPost(request); } else if ((transmissionMethod & HttpDeliveryMethods.GetRequest) != 0) { httpRequest = InitializeRequestAsGet(request); @@ -265,7 +267,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// </summary> /// <param name="source">The dictionary with names and values to encode.</param> /// <param name="destination">The dictionary to add the encoded pairs to.</param> - private static void UriEscapeParameters(IDictionary<string, string> source, IDictionary<string, string> destination) { + private static void UriEscapeParameters(IEnumerable<KeyValuePair<string, string>> source, IDictionary<string, string> destination) { Contract.Requires<ArgumentNullException>(source != null); Contract.Requires<ArgumentNullException>(destination != null); @@ -344,13 +346,10 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { // extra data. If there isn't any extra data, the caller must do this themselves. var requestMessageWithBinaryData = requestMessage as IMessageWithBinaryData; if (requestMessageWithBinaryData != null && requestMessageWithBinaryData.SendAsMultipart) { + // Include the binary data in the multipart entity, and any standard text extra message data. + // The standard declared message parts are included in the authorization header. var multiPartFields = new List<MultipartPostPart>(requestMessageWithBinaryData.BinaryData); - - // When sending multi-part, all data gets send as multi-part -- even the non-binary data - // unless we're using the HTTP authorization header. - if ((requestMessage.HttpMethods & HttpDeliveryMethods.AuthorizationHeaderRequest) == 0) { - multiPartFields.AddRange(fields.Select(field => MultipartPostPart.CreateFormPart(field.Key, field.Value))); - } + multiPartFields.AddRange(requestMessage.ExtraData.Select(field => MultipartPostPart.CreateFormPart(field.Key, field.Value))); this.SendParametersInEntityAsMultiPart(httpRequest, multiPartFields); } else { ErrorUtilities.VerifyProtocol(requestMessageWithBinaryData == null || requestMessageWithBinaryData.BinaryData.Count == 0, MessagingStrings.BinaryDataRequiresMultipart); diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs index 084a622..634fd07 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBase.cs @@ -10,6 +10,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { using System.Collections.Specialized; using System.Diagnostics.Contracts; using System.Globalization; + using System.Linq; using System.Text; using System.Web; using DotNetOpenAuth.Messaging; @@ -157,15 +158,25 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { signatureBaseStringElements.Add(message.HttpMethod.ToUpperInvariant()); - // We only include the message parts in the signature base string if the message is - // NOT going out as multi-part (unless the text parts are in the HTTP header). - IDictionary<string, string> encodedDictionary; + // For multipart POST messages, only include the message parts that are NOT + // in the POST entity (those parts that may appear in an OAuth authorization header). + var encodedDictionary = new Dictionary<string, string>(); + IEnumerable<KeyValuePair<string, string>> partsToInclude = Enumerable.Empty<KeyValuePair<string, string>>(); var binaryMessage = message as IMessageWithBinaryData; - if (binaryMessage != null && binaryMessage.SendAsMultipart && - (binaryMessage.HttpMethods & (HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest)) == HttpDeliveryMethods.PostRequest) { - encodedDictionary = new Dictionary<string, string>(); + if (binaryMessage != null && binaryMessage.SendAsMultipart) { + HttpDeliveryMethods authHeaderInUseFlags = HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest; + ErrorUtilities.VerifyInternal((binaryMessage.HttpMethods & authHeaderInUseFlags) == authHeaderInUseFlags, "We should only be sending multipart messages with an authorization header so the critical message parts can be signed."); + + // Include the declared keys in the signature as those will be signable. + // Cache in local variable to avoid recalculating DeclaredKeys in the delegate. + ICollection<string> declaredKeys = messageDictionary.DeclaredKeys; + partsToInclude = messageDictionary.Where(pair => declaredKeys.Contains(pair.Key)); } else { - encodedDictionary = OAuthChannel.GetUriEscapedParameters(messageDictionary); + partsToInclude = messageDictionary; + } + + foreach (var pair in OAuthChannel.GetUriEscapedParameters(partsToInclude)) { + encodedDictionary[pair.Key] = pair.Value; } // An incoming message will already have included the query and form parameters diff --git a/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs b/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs index 4d1c0e0..f3231f0 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs @@ -64,7 +64,7 @@ namespace DotNetOpenAuth.OAuth.Messages { /// Gets a value indicating whether this message should be sent as multi-part POST. /// </summary> public bool SendAsMultipart { - get { return this.BinaryData.Count > 0; } + get { return this.HttpMethod == "POST" && this.BinaryData.Count > 0; } } #endregion diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs index 3593446..fd74bcb 100644 --- a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs +++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:2.0.50727.4918 +// Runtime Version:2.0.50727.4927 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -115,29 +115,38 @@ namespace DotNetOpenAuth.OAuth { } /// <summary> - /// Looks up a localized string similar to Use of the OpenID+OAuth extension requires that the token manager in use implement the {0} interface.. + /// Looks up a localized string similar to This OAuth service provider requires OAuth consumers to implement OAuth {0}, but this consumer appears to only support {1}.. /// </summary> - internal static string OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface { + internal static string MinimumConsumerVersionRequirementNotMet { get { - return ResourceManager.GetString("OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface", resourceCulture); + return ResourceManager.GetString("MinimumConsumerVersionRequirementNotMet", resourceCulture); } } /// <summary> - /// Looks up a localized string similar to The OpenID Relying Party's realm is not recognized as belonging to the OAuth Consumer identified by the consumer key given.. + /// Looks up a localized string similar to Cannot send OAuth message as multipart POST without an authorization HTTP header because sensitive data would not be signed.. /// </summary> - internal static string OpenIdOAuthRealmConsumerKeyDoNotMatch { + internal static string MultipartPostMustBeUsedWithAuthHeader { get { - return ResourceManager.GetString("OpenIdOAuthRealmConsumerKeyDoNotMatch", resourceCulture); + return ResourceManager.GetString("MultipartPostMustBeUsedWithAuthHeader", resourceCulture); } } /// <summary> - /// Looks up a localized string similar to This OAuth service provider requires OAuth consumers to implement OAuth {0}, but this consumer appears to only support {1}.. + /// Looks up a localized string similar to Use of the OpenID+OAuth extension requires that the token manager in use implement the {0} interface.. /// </summary> - internal static string MinimumConsumerVersionRequirementNotMet { + internal static string OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface { get { - return ResourceManager.GetString("MinimumConsumerVersionRequirementNotMet", resourceCulture); + return ResourceManager.GetString("OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The OpenID Relying Party's realm is not recognized as belonging to the OAuth Consumer identified by the consumer key given.. + /// </summary> + internal static string OpenIdOAuthRealmConsumerKeyDoNotMatch { + get { + return ResourceManager.GetString("OpenIdOAuthRealmConsumerKeyDoNotMatch", resourceCulture); } } diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx index bbeeda9..34b314b 100644 --- a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx +++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx @@ -138,6 +138,9 @@ <data name="MinimumConsumerVersionRequirementNotMet" xml:space="preserve"> <value>This OAuth service provider requires OAuth consumers to implement OAuth {0}, but this consumer appears to only support {1}.</value> </data> + <data name="MultipartPostMustBeUsedWithAuthHeader" xml:space="preserve"> + <value>Cannot send OAuth message as multipart POST without an authorization HTTP header because sensitive data would not be signed.</value> + </data> <data name="OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface" xml:space="preserve"> <value>Use of the OpenID+OAuth extension requires that the token manager in use implement the {0} interface.</value> </data> |