diff options
Diffstat (limited to 'src/DotNetOpenAuth.Core')
25 files changed, 615 insertions, 102 deletions
diff --git a/src/DotNetOpenAuth.Core/Assumes.cs b/src/DotNetOpenAuth.Core/Assumes.cs index f29f09f..151fa2f 100644 --- a/src/DotNetOpenAuth.Core/Assumes.cs +++ b/src/DotNetOpenAuth.Core/Assumes.cs @@ -58,6 +58,14 @@ namespace DotNetOpenAuth { } /// <summary> + /// Throws an internal error exception. + /// </summary> + /// <returns>Nothing. This method always throws.</returns> + internal static Exception NotReachable() { + throw new InternalErrorException(); + } + + /// <summary> /// An internal error exception that should never be caught. /// </summary> [SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic", Justification = "This exception should never be caught.")] diff --git a/src/DotNetOpenAuth.Core/Configuration/DotNetOpenAuth.xsd b/src/DotNetOpenAuth.Core/Configuration/DotNetOpenAuth.xsd index d193776..74d4db4 100644 --- a/src/DotNetOpenAuth.Core/Configuration/DotNetOpenAuth.xsd +++ b/src/DotNetOpenAuth.Core/Configuration/DotNetOpenAuth.xsd @@ -479,12 +479,19 @@ <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="add"> <xs:complexType> - <xs:attribute name="name" type="xs:string" use="required" /> + <xs:attribute name="type" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> + The fully-qualified name of the type that implements the IIdentifierDiscoveryService interface. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="xaml" type="xs:string" use="optional" /> </xs:complexType> </xs:element> <xs:element name="remove"> <xs:complexType> - <xs:attribute name="name" type="xs:string" use="required" /> + <xs:attribute name="type" type="xs:string" use="required" /> </xs:complexType> </xs:element> <xs:element name="clear"> @@ -898,6 +905,84 @@ </xs:choice> </xs:complexType> </xs:element> + <xs:element name="oauth2"> + <xs:annotation> + <xs:documentation> + Settings OAuth 2 clients, authorization servers and resource servers. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element name="client"> + <xs:annotation> + <xs:documentation> + Settings applicable to OAuth 2 Clients. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + </xs:choice> + </xs:complexType> + </xs:element> + <xs:element name="authorizationServer"> + <xs:annotation> + <xs:documentation> + Settings applicable to OAuth 2 Authorization Servers. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element name="clientAuthenticationModules"> + <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + <xs:element name="add"> + <xs:complexType> + <xs:attribute name="type" type="xs:string" use="optional"> + <xs:annotation> + <xs:documentation> + The fully-qualified name of the ClientAuthenticationModule-derived type. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="xaml" type="xs:string" use="optional" /> + </xs:complexType> + </xs:element> + <xs:element name="remove"> + <xs:complexType> + <xs:attribute name="type" type="xs:string" use="required"> + <xs:annotation> + <xs:documentation> + The fully-qualified name of the ClientAuthenticationModule-derived type. + </xs:documentation> + </xs:annotation> + </xs:attribute> + </xs:complexType> + </xs:element> + <xs:element name="clear"> + <xs:complexType> + <!--tag is empty--> + </xs:complexType> + </xs:element> + </xs:choice> + </xs:complexType> + </xs:element> + </xs:choice> + </xs:complexType> + </xs:element> + <xs:element name="resourceServer"> + <xs:annotation> + <xs:documentation> + Settings applicable to OAuth 2 Resource Servers. + </xs:documentation> + </xs:annotation> + <xs:complexType> + <xs:choice minOccurs="0" maxOccurs="unbounded"> + </xs:choice> + </xs:complexType> + </xs:element> + </xs:choice> + </xs:complexType> + </xs:element> <xs:element name="reporting"> <xs:annotation> <xs:documentation> diff --git a/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj b/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj index 447a3c5..5e079a0 100644 --- a/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj +++ b/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj @@ -19,6 +19,7 @@ </PropertyGroup> <ItemGroup> <Compile Include="Assumes.cs" /> + <Compile Include="Messaging\Base64WebEncoder.cs" /> <Compile Include="Messaging\Bindings\AsymmetricCryptoKeyStoreWrapper.cs" /> <Compile Include="Messaging\Bindings\CryptoKey.cs" /> <Compile Include="Messaging\Bindings\CryptoKeyCollisionException.cs" /> @@ -29,6 +30,8 @@ <Compile Include="Messaging\ChannelContract.cs" /> <Compile Include="Messaging\DataBagFormatterBase.cs" /> <Compile Include="Messaging\HttpRequestHeaders.cs" /> + <Compile Include="Messaging\IHttpDirectRequest.cs" /> + <Compile Include="Messaging\IHttpDirectRequestContract.cs" /> <Compile Include="Messaging\IHttpIndirectResponse.cs" /> <Compile Include="Messaging\IMessageOriginalPayload.cs" /> <Compile Include="Messaging\DirectWebRequestOptions.cs" /> @@ -53,6 +56,7 @@ <Compile Include="Messaging\MultipartPostPart.cs" /> <Compile Include="Messaging\NetworkDirectWebResponse.cs" /> <Compile Include="Messaging\OutgoingWebResponseActionResult.cs" /> + <Compile Include="Messaging\ProtocolFaultResponseException.cs" /> <Compile Include="Messaging\Reflection\IMessagePartEncoder.cs" /> <Compile Include="Messaging\Reflection\IMessagePartNullEncoder.cs" /> <Compile Include="Messaging\Reflection\IMessagePartOriginalEncoder.cs" /> diff --git a/src/DotNetOpenAuth.Core/Messaging/Base64WebEncoder.cs b/src/DotNetOpenAuth.Core/Messaging/Base64WebEncoder.cs new file mode 100644 index 0000000..135e650 --- /dev/null +++ b/src/DotNetOpenAuth.Core/Messaging/Base64WebEncoder.cs @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------- +// <copyright file="Base64WebEncoder.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Messaging { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using DotNetOpenAuth.Messaging.Reflection; + + /// <summary> + /// A message part encoder that translates between <c>byte[]</c> and base64web encoded strings. + /// </summary> + internal class Base64WebEncoder : IMessagePartEncoder { + /// <summary> + /// Encodes the specified value. + /// </summary> + /// <param name="value">The value. Guaranteed to never be null.</param> + /// <returns>The <paramref name="value"/> in string form, ready for message transport.</returns> + public string Encode(object value) { + return MessagingUtilities.ConvertToBase64WebSafeString((byte[])value); + } + + /// <summary> + /// Decodes the specified value. + /// </summary> + /// <param name="value">The string value carried by the transport. Guaranteed to never be null, although it may be empty.</param> + /// <returns>The deserialized form of the given string.</returns> + /// <exception cref="FormatException">Thrown when the string value given cannot be decoded into the required object type.</exception> + public object Decode(string value) { + return MessagingUtilities.FromBase64WebSafeString(value); + } + } +} diff --git a/src/DotNetOpenAuth.Core/Messaging/Channel.cs b/src/DotNetOpenAuth.Core/Messaging/Channel.cs index c58702c..672a942 100644 --- a/src/DotNetOpenAuth.Core/Messaging/Channel.cs +++ b/src/DotNetOpenAuth.Core/Messaging/Channel.cs @@ -38,6 +38,16 @@ namespace DotNetOpenAuth.Messaging { internal static readonly Encoding PostEntityEncoding = new UTF8Encoding(false); /// <summary> + /// A default set of XML dictionary reader quotas that are relatively safe from causing unbounded memory consumption. + /// </summary> + internal static readonly XmlDictionaryReaderQuotas DefaultUntrustedXmlDictionaryReaderQuotas = new XmlDictionaryReaderQuotas { + MaxArrayLength = 1, + MaxDepth = 2, + MaxBytesPerRead = 8 * 1024, + MaxStringContentLength = 16 * 1024, + }; + + /// <summary> /// The content-type used on HTTP POST requests where the POST entity is a /// URL-encoded series of key=value pairs. /// </summary> @@ -143,18 +153,16 @@ namespace DotNetOpenAuth.Messaging { /// A class prepared to analyze incoming messages and indicate what concrete /// message types can deserialize from it. /// </param> - /// <param name="bindingElements">The binding elements to use in sending and receiving messages.</param> + /// <param name="bindingElements"> + /// The binding elements to use in sending and receiving messages. + /// The order they are provided is used for outgoing messgaes, and reversed for incoming messages. + /// </param> protected Channel(IMessageFactory messageTypeProvider, params IChannelBindingElement[] bindingElements) { Requires.NotNull(messageTypeProvider, "messageTypeProvider"); this.messageTypeProvider = messageTypeProvider; this.WebRequestHandler = new StandardWebRequestHandler(); - this.XmlDictionaryReaderQuotas = new XmlDictionaryReaderQuotas { - MaxArrayLength = 1, - MaxDepth = 2, - MaxBytesPerRead = 8 * 1024, - MaxStringContentLength = 16 * 1024, - }; + this.XmlDictionaryReaderQuotas = DefaultUntrustedXmlDictionaryReaderQuotas; this.outgoingBindingElements = new List<IChannelBindingElement>(ValidateAndPrepareBindingElements(bindingElements)); this.incomingBindingElements = new List<IChannelBindingElement>(this.outgoingBindingElements); @@ -475,6 +483,14 @@ namespace DotNetOpenAuth.Messaging { IDirectedProtocolMessage requestMessage = this.ReadFromRequestCore(httpRequest); if (requestMessage != null) { Logger.Channel.DebugFormat("Incoming request received: {0}", requestMessage.GetType().Name); + + var directRequest = requestMessage as IHttpDirectRequest; + if (directRequest != null) { + foreach (string header in httpRequest.Headers) { + directRequest.Headers[header] = httpRequest.Headers[header]; + } + } + this.ProcessIncomingMessage(requestMessage); } @@ -714,6 +730,13 @@ namespace DotNetOpenAuth.Messaging { Requires.True(request.Recipient != null, "request", MessagingStrings.DirectedMessageMissingRecipient); HttpWebRequest webRequest = this.CreateHttpRequest(request); + var directRequest = request as IHttpDirectRequest; + if (directRequest != null) { + foreach (string header in directRequest.Headers) { + webRequest.Headers[header] = directRequest.Headers[header]; + } + } + IDictionary<string, string> responseFields; IDirectResponseProtocolMessage responseMessage; @@ -973,17 +996,7 @@ namespace DotNetOpenAuth.Messaging { [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No apparent problem. False positive?")] protected virtual string SerializeAsJson(IMessage message) { Requires.NotNull(message, "message"); - - MessageDictionary messageDictionary = this.MessageDescriptions.GetAccessor(message); - using (var memoryStream = new MemoryStream()) { - using (var jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(memoryStream, Encoding.UTF8)) { - MessageSerializer.Serialize(messageDictionary, jsonWriter); - jsonWriter.Flush(); - } - - string json = Encoding.UTF8.GetString(memoryStream.ToArray()); - return json; - } + return MessagingUtilities.SerializeAsJson(message, this.MessageDescriptions); } /// <summary> @@ -1079,6 +1092,7 @@ namespace DotNetOpenAuth.Messaging { UriBuilder builder = new UriBuilder(requestMessage.Recipient); MessagingUtilities.AppendQueryArgs(builder, fields); HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(builder.Uri); + this.PrepareHttpWebRequest(httpRequest); return httpRequest; } @@ -1119,6 +1133,7 @@ namespace DotNetOpenAuth.Messaging { var fields = messageAccessor.Serialize(); var httpRequest = (HttpWebRequest)WebRequest.Create(requestMessage.Recipient); + this.PrepareHttpWebRequest(httpRequest); httpRequest.CachePolicy = this.CachePolicy; httpRequest.Method = "POST"; @@ -1296,6 +1311,14 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Performs additional processing on an outgoing web request before it is sent to the remote server. + /// </summary> + /// <param name="request">The request.</param> + protected virtual void PrepareHttpWebRequest(HttpWebRequest request) { + Requires.NotNull(request, "request"); + } + + /// <summary> /// Customizes the binding element order for outgoing and incoming messages. /// </summary> /// <param name="outgoingOrder">The outgoing order.</param> diff --git a/src/DotNetOpenAuth.Core/Messaging/DataBag.cs b/src/DotNetOpenAuth.Core/Messaging/DataBag.cs index c9c3415..0800840 100644 --- a/src/DotNetOpenAuth.Core/Messaging/DataBag.cs +++ b/src/DotNetOpenAuth.Core/Messaging/DataBag.cs @@ -14,7 +14,7 @@ namespace DotNetOpenAuth.Messaging { /// A collection of message parts that will be serialized into a single string, /// to be set into a larger message. /// </summary> - internal abstract class DataBag : IMessage { + public abstract class DataBag : IMessage { /// <summary> /// The default version for DataBags. /// </summary> @@ -105,7 +105,7 @@ namespace DotNetOpenAuth.Messaging { /// </remarks> [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Accessed by reflection")] [MessagePart("t", IsRequired = true, AllowEmpty = false)] - private Type BagType { + protected virtual Type BagType { get { return this.GetType(); } } diff --git a/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs b/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs index 9d4b93e..c9ceb81 100644 --- a/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs +++ b/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs @@ -22,7 +22,7 @@ namespace DotNetOpenAuth.Messaging { /// A serializer for <see cref="DataBag"/>-derived types /// </summary> /// <typeparam name="T">The DataBag-derived type that is to be serialized/deserialized.</typeparam> - internal abstract class DataBagFormatterBase<T> : IDataBagFormatter<T> where T : DataBag, new() { + internal abstract class DataBagFormatterBase<T> : IDataBagFormatter<T> where T : DataBag { /// <summary> /// The message description cache to use for data bag types. /// </summary> @@ -146,6 +146,8 @@ namespace DotNetOpenAuth.Messaging { /// <returns>A non-null, non-empty value.</returns> [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No apparent problem. False positive?")] public string Serialize(T message) { + Requires.NotNull(message, "message"); + message.UtcCreationDate = DateTime.UtcNow; if (this.decodeOnceOnly != null) { @@ -190,14 +192,13 @@ namespace DotNetOpenAuth.Messaging { /// <summary> /// Deserializes a <see cref="DataBag"/>, including decompression, decryption, signature and nonce validation where applicable. /// </summary> + /// <param name="message">The instance to initialize with deserialized data.</param> /// <param name="containingMessage">The message that contains the <see cref="DataBag"/> serialized value. Must not be null.</param> /// <param name="value">The serialized form of the <see cref="DataBag"/> to deserialize. Must not be null or empty.</param> /// <param name="messagePartName">The name of the parameter whose value is to be deserialized. Used for error message generation.</param> - /// <returns> - /// The deserialized value. Never null. - /// </returns> [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No apparent problem. False positive?")] - public T Deserialize(IProtocolMessage containingMessage, string value, string messagePartName) { + public void Deserialize(T message, IProtocolMessage containingMessage, string value, string messagePartName) { + Requires.NotNull(message, "message"); Requires.NotNull(containingMessage, "containingMessage"); Requires.NotNullOrEmpty(value, "value"); Requires.NotNullOrEmpty(messagePartName, "messagePartName"); @@ -209,7 +210,7 @@ namespace DotNetOpenAuth.Messaging { value = valueWithoutHandle; } - var message = new T { ContainingMessage = containingMessage }; + message.ContainingMessage = containingMessage; byte[] data = MessagingUtilities.FromBase64WebSafeString(value); byte[] signature = null; @@ -254,8 +255,6 @@ namespace DotNetOpenAuth.Messaging { } ((IMessage)message).EnsureValidMessage(); - - return message; } /// <summary> diff --git a/src/DotNetOpenAuth.Core/Messaging/ErrorUtilities.cs b/src/DotNetOpenAuth.Core/Messaging/ErrorUtilities.cs index f499d67..2237cc7 100644 --- a/src/DotNetOpenAuth.Core/Messaging/ErrorUtilities.cs +++ b/src/DotNetOpenAuth.Core/Messaging/ErrorUtilities.cs @@ -193,17 +193,17 @@ namespace DotNetOpenAuth.Messaging { /// Throws a <see cref="ProtocolException"/> if some <paramref name="condition"/> evaluates to false. /// </summary> /// <param name="condition">True to do nothing; false to throw the exception.</param> - /// <param name="message">The error message for the exception.</param> + /// <param name="unformattedMessage">The error message for the exception.</param> /// <param name="args">The string formatting arguments, if any.</param> /// <exception cref="ProtocolException">Thrown if <paramref name="condition"/> evaluates to <c>false</c>.</exception> [Pure] - internal static void VerifyProtocol(bool condition, string message, params object[] args) { + internal static void VerifyProtocol(bool condition, string unformattedMessage, params object[] args) { Requires.NotNull(args, "args"); Contract.Ensures(condition); Contract.EnsuresOnThrow<ProtocolException>(!condition); - Contract.Assume(message != null); + Contract.Assume(unformattedMessage != null); if (!condition) { - var exception = new ProtocolException(string.Format(CultureInfo.CurrentCulture, message, args)); + var exception = new ProtocolException(string.Format(CultureInfo.CurrentCulture, unformattedMessage, args)); if (Logger.Messaging.IsErrorEnabled) { Logger.Messaging.Error( string.Format( @@ -220,7 +220,7 @@ namespace DotNetOpenAuth.Messaging { /// <summary> /// Throws a <see cref="ProtocolException"/>. /// </summary> - /// <param name="message">The message to set in the exception.</param> + /// <param name="unformattedMessage">The message to set in the exception.</param> /// <param name="args">The formatting arguments of the message.</param> /// <returns> /// An InternalErrorException, which may be "thrown" by the caller in order @@ -229,10 +229,10 @@ namespace DotNetOpenAuth.Messaging { /// </returns> /// <exception cref="ProtocolException">Always thrown.</exception> [Pure] - internal static Exception ThrowProtocol(string message, params object[] args) { + internal static Exception ThrowProtocol(string unformattedMessage, params object[] args) { Requires.NotNull(args, "args"); - Contract.Assume(message != null); - VerifyProtocol(false, message, args); + Contract.Assume(unformattedMessage != null); + VerifyProtocol(false, unformattedMessage, args); // we never reach here, but this allows callers to "throw" this method. return new InternalErrorException(); diff --git a/src/DotNetOpenAuth.Core/Messaging/HttpRequestHeaders.cs b/src/DotNetOpenAuth.Core/Messaging/HttpRequestHeaders.cs index 9579a81..dad6bf6 100644 --- a/src/DotNetOpenAuth.Core/Messaging/HttpRequestHeaders.cs +++ b/src/DotNetOpenAuth.Core/Messaging/HttpRequestHeaders.cs @@ -20,6 +20,11 @@ namespace DotNetOpenAuth.Messaging { internal const string Authorization = "Authorization"; /// <summary> + /// The WWW-Authenticate header, which is included in HTTP 401 Unauthorized responses to help the client know which authorization schemes are supported. + /// </summary> + internal const string WwwAuthenticate = "WWW-Authenticate"; + + /// <summary> /// The Content-Type header, which specifies the MIME type of the accompanying body data. /// </summary> internal const string ContentType = "Content-Type"; diff --git a/src/DotNetOpenAuth.Core/Messaging/HttpRequestInfo.cs b/src/DotNetOpenAuth.Core/Messaging/HttpRequestInfo.cs index 0f60e04..f613dc5 100644 --- a/src/DotNetOpenAuth.Core/Messaging/HttpRequestInfo.cs +++ b/src/DotNetOpenAuth.Core/Messaging/HttpRequestInfo.cs @@ -90,7 +90,7 @@ namespace DotNetOpenAuth.Messaging { this.requestUri = requestUri; this.form = form ?? new NameValueCollection(); this.queryString = HttpUtility.ParseQueryString(requestUri.Query); - this.headers = headers ?? new NameValueCollection(); + this.headers = headers ?? new WebHeaderCollection(); this.serverVariables = new NameValueCollection(); } diff --git a/src/DotNetOpenAuth.Core/Messaging/IDataBagFormatter.cs b/src/DotNetOpenAuth.Core/Messaging/IDataBagFormatter.cs index 9086ee9..923773e 100644 --- a/src/DotNetOpenAuth.Core/Messaging/IDataBagFormatter.cs +++ b/src/DotNetOpenAuth.Core/Messaging/IDataBagFormatter.cs @@ -13,7 +13,7 @@ namespace DotNetOpenAuth.Messaging { /// </summary> /// <typeparam name="T">The DataBag-derived type that is to be serialized/deserialized.</typeparam> [ContractClass(typeof(IDataBagFormatterContract<>))] - internal interface IDataBagFormatter<T> where T : DataBag, new() { + internal interface IDataBagFormatter<in T> where T : DataBag { /// <summary> /// Serializes the specified message. /// </summary> @@ -24,13 +24,11 @@ namespace DotNetOpenAuth.Messaging { /// <summary> /// Deserializes a <see cref="DataBag"/>. /// </summary> + /// <param name="message">The instance to deserialize into</param> /// <param name="containingMessage">The message that contains the <see cref="DataBag"/> serialized value. Must not be null.</param> /// <param name="data">The serialized form of the <see cref="DataBag"/> to deserialize. Must not be null or empty.</param> /// <param name="messagePartName">The name of the parameter whose value is to be deserialized. Used for error message generation.</param> - /// <returns> - /// The deserialized value. Never null. - /// </returns> - T Deserialize(IProtocolMessage containingMessage, string data, string messagePartName); + void Deserialize(T message, IProtocolMessage containingMessage, string data, string messagePartName); } /// <summary> @@ -62,13 +60,12 @@ namespace DotNetOpenAuth.Messaging { /// <summary> /// Deserializes a <see cref="DataBag"/>. /// </summary> + /// <param name="message">The instance to deserialize into</param> /// <param name="containingMessage">The message that contains the <see cref="DataBag"/> serialized value. Must not be nulll.</param> /// <param name="data">The serialized form of the <see cref="DataBag"/> to deserialize. Must not be null or empty.</param> /// <param name="messagePartName">Name of the message part whose value is to be deserialized. Used for exception messages.</param> - /// <returns> - /// The deserialized value. Never null. - /// </returns> - T IDataBagFormatter<T>.Deserialize(IProtocolMessage containingMessage, string data, string messagePartName) { + void IDataBagFormatter<T>.Deserialize(T message, IProtocolMessage containingMessage, string data, string messagePartName) { + Requires.NotNull(message, "message"); Requires.NotNull(containingMessage, "containingMessage"); Requires.NotNullOrEmpty(data, "data"); Requires.NotNullOrEmpty(messagePartName, "messagePartName"); diff --git a/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs b/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs new file mode 100644 index 0000000..7153334 --- /dev/null +++ b/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs @@ -0,0 +1,22 @@ +//----------------------------------------------------------------------- +// <copyright file="IHttpDirectRequest.cs" company="Outercurve Foundation"> +// Copyright (c) Outercurve Foundation. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Messaging { + using System.Diagnostics.Contracts; + using System.Net; + + /// <summary> + /// An interface that allows direct request messages to capture the details of the HTTP request they arrived on. + /// </summary> + [ContractClass(typeof(IHttpDirectRequestContract))] + public interface IHttpDirectRequest : IMessage { + /// <summary> + /// Gets the HTTP headers of the request. + /// </summary> + /// <value>May be an empty collection, but must not be <c>null</c>.</value> + WebHeaderCollection Headers { get; } + } +} diff --git a/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequestContract.cs b/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequestContract.cs new file mode 100644 index 0000000..cfde6cf --- /dev/null +++ b/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequestContract.cs @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------- +// <copyright file="IHttpDirectRequestContract.cs" company="Outercurve Foundation"> +// Copyright (c) Outercurve Foundation. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Messaging { + using System; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using System.Linq; + using System.Net; + using System.Text; + + /// <summary> + /// Contract class for the <see cref="IHttpDirectRequest"/> interface. + /// </summary> + [ContractClassFor(typeof(IHttpDirectRequest))] + public abstract class IHttpDirectRequestContract : IHttpDirectRequest { + #region IHttpDirectRequest Members + + /// <summary> + /// Gets the HTTP headers of the request. + /// </summary> + /// <value>May be an empty collection, but must not be <c>null</c>.</value> + WebHeaderCollection IHttpDirectRequest.Headers { + get { + Contract.Ensures(Contract.Result<WebHeaderCollection>() != null); + throw new NotImplementedException(); + } + } + + #endregion + + #region IMessage Members + + /// <summary> + /// Gets the version of the protocol or extension this message is prepared to implement. + /// </summary> + /// <remarks> + /// Implementations of this interface should ensure that this property never returns null. + /// </remarks> + Version IMessage.Version { + get { throw new NotImplementedException(); } + } + + /// <summary> + /// Gets the extra, non-standard Protocol parameters included in the message. + /// </summary> + /// <remarks> + /// Implementations of this interface should ensure that this property never returns null. + /// </remarks> + IDictionary<string, string> IMessage.ExtraData { + get { throw new NotImplementedException(); } + } + + /// <summary> + /// Checks the message state for conformity to the protocol specification + /// and throws an exception if the message is invalid. + /// </summary> + /// <remarks> + /// <para>Some messages have required fields, or combinations of fields that must relate to each other + /// in specialized ways. After deserializing a message, this method checks the state of the + /// message to see if it conforms to the protocol.</para> + /// <para>Note that this property should <i>not</i> check signatures or perform any state checks + /// outside this scope of this particular message.</para> + /// </remarks> + /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception> + void IMessage.EnsureValidMessage() { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/src/DotNetOpenAuth.Core/Messaging/MessageSerializer.cs b/src/DotNetOpenAuth.Core/Messaging/MessageSerializer.cs index bdca190..15df48a 100644 --- a/src/DotNetOpenAuth.Core/Messaging/MessageSerializer.cs +++ b/src/DotNetOpenAuth.Core/Messaging/MessageSerializer.cs @@ -74,7 +74,7 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> - /// Reads the data from a message instance and writes a XML/JSON encoding of it. + /// Reads the data from a message instance and writes an XML/JSON encoding of it. /// </summary> /// <param name="messageDictionary">The message to be serialized.</param> /// <param name="writer">The writer to use for the serialized form.</param> diff --git a/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs index b26deeb..e821953 100644 --- a/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs +++ b/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs @@ -16,11 +16,13 @@ namespace DotNetOpenAuth.Messaging { using System.Linq; using System.Net; using System.Net.Mime; + using System.Runtime.Serialization.Json; using System.Security; using System.Security.Cryptography; using System.Text; using System.Web; using System.Web.Mvc; + using System.Xml; using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.Messaging.Reflection; @@ -135,6 +137,21 @@ namespace DotNetOpenAuth.Messaging { }; /// <summary> + /// The available compression algorithms. + /// </summary> + internal enum CompressionMethod { + /// <summary> + /// The Deflate algorithm. + /// </summary> + Deflate, + + /// <summary> + /// The GZip algorithm. + /// </summary> + Gzip, + } + + /// <summary> /// Transforms an OutgoingWebResponse to an MVC-friendly ActionResult. /// </summary> /// <param name="response">The response to send to the user agent.</param> @@ -290,6 +307,56 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Compares to string values for ordinal equality in such a way that its execution time does not depend on how much of the value matches. + /// </summary> + /// <param name="value1">The first value.</param> + /// <param name="value2">The second value.</param> + /// <returns>A value indicating whether the two strings share ordinal equality.</returns> + /// <remarks> + /// In signature equality checks, a difference in execution time based on how many initial characters match MAY + /// be used as an attack to figure out the expected signature. It is therefore important to make a signature + /// equality check's execution time independent of how many characters match the expected value. + /// See http://codahale.com/a-lesson-in-timing-attacks/ for more information. + /// </remarks> + public static bool EqualsConstantTime(string value1, string value2) { + // If exactly one value is null, they don't match. + if (value1 == null ^ value2 == null) { + return false; + } + + // If both values are null (since if one is at this point then they both are), it's a match. + if (value1 == null) { + return true; + } + + if (value1.Length != value2.Length) { + return false; + } + + // This looks like a pretty crazy way to compare values, but it provides a constant time equality check, + // and is more resistant to compiler optimizations than simply setting a boolean flag and returning the boolean after the loop. + int result = 0; + for (int i = 0; i < value1.Length; i++) { + result |= value1[i] ^ value2[i]; + } + + return result == 0; + } + + /// <summary> + /// Gets the URL to the root of a web site, which may include a virtual directory path. + /// </summary> + /// <returns>An absolute URI.</returns> + internal static Uri GetWebRoot() { + HttpRequestBase requestInfo = new HttpRequestWrapper(HttpContext.Current.Request); + UriBuilder realmUrl = new UriBuilder(requestInfo.GetPublicFacingUrl()); + realmUrl.Path = HttpContext.Current.Request.ApplicationPath; + realmUrl.Query = null; + realmUrl.Fragment = null; + return realmUrl.Uri; + } + + /// <summary> /// Clears any existing elements in a collection and fills the collection with a given set of values. /// </summary> /// <typeparam name="T">The type of value kept in the collection.</typeparam> @@ -756,6 +823,12 @@ namespace DotNetOpenAuth.Messaging { var cryptoKeyPair = cryptoKeyStore.GetKeys(bucket).FirstOrDefault(pair => pair.Value.Key.Length == keySize / 8); if (cryptoKeyPair.Value == null || cryptoKeyPair.Value.ExpiresUtc < DateTime.UtcNow + minimumRemainingLife) { // No key exists with enough remaining life for the required purpose. Create a new key. + if (cryptoKeyPair.Value == null) { + Logger.Messaging.InfoFormat("{0}.GetKeys returned no keys for bucket \"{1}\" with the required key length of {2} bits. A new key will be created", typeof(ICryptoKeyStore), bucket, keySize); + } else { + Logger.Messaging.InfoFormat("The first key returned by {0}.GetKeys for bucket \"{1}\" with the required key length of {2} bits was too near expiry to use. A new key will be created", typeof(ICryptoKeyStore), bucket, keySize); + } + ErrorUtilities.VerifyHost(minimumRemainingLife <= SymmetricSecretKeyLifespan, "Unable to create a new symmetric key with the required lifespan of {0} because it is beyond the limit of {1}.", minimumRemainingLife, SymmetricSecretKeyLifespan); byte[] secret = GetCryptoRandomData(keySize / 8); DateTime expires = DateTime.UtcNow + SymmetricSecretKeyLifespan; @@ -770,6 +843,7 @@ namespace DotNetOpenAuth.Messaging { cryptoKeyStore.StoreKey(bucket, handle, cryptoKey); } catch (CryptoKeyCollisionException) { ErrorUtilities.VerifyInternal(++failedAttempts < 3, "Unable to derive a unique handle to a private symmetric key."); + Logger.Messaging.Warn("A randomly generated crypto key handle collided with an existing handle. Another randomly generated handle will be attempted till the retry count is met."); goto tryAgain; } } @@ -781,19 +855,36 @@ namespace DotNetOpenAuth.Messaging { /// Compresses a given buffer. /// </summary> /// <param name="buffer">The buffer to compress.</param> + /// <param name="method">The compression algorithm to use.</param> /// <returns>The compressed data.</returns> [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "This Dispose is safe.")] [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No apparent problem. False positive?")] - internal static byte[] Compress(byte[] buffer) { + internal static byte[] Compress(byte[] buffer, CompressionMethod method = CompressionMethod.Deflate) { Requires.NotNull(buffer, "buffer"); Contract.Ensures(Contract.Result<byte[]>() != null); using (var ms = new MemoryStream()) { - using (var compressingStream = new DeflateStream(ms, CompressionMode.Compress, true)) { + Stream compressingStream = null; + try { + switch (method) { + case CompressionMethod.Deflate: + compressingStream = new DeflateStream(ms, CompressionMode.Compress, true); + break; + case CompressionMethod.Gzip: + compressingStream = new GZipStream(ms, CompressionMode.Compress, true); + break; + default: + Requires.InRange(false, "method"); + break; + } + compressingStream.Write(buffer, 0, buffer.Length); + return ms.ToArray(); + } finally { + if (compressingStream != null) { + compressingStream.Dispose(); + } } - - return ms.ToArray(); } } @@ -801,17 +892,35 @@ namespace DotNetOpenAuth.Messaging { /// Decompresses a given buffer. /// </summary> /// <param name="buffer">The buffer to decompress.</param> + /// <param name="method">The compression algorithm used.</param> /// <returns>The decompressed data.</returns> [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No apparent problem. False positive?")] [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "This Dispose is safe.")] - internal static byte[] Decompress(byte[] buffer) { + internal static byte[] Decompress(byte[] buffer, CompressionMethod method = CompressionMethod.Deflate) { Requires.NotNull(buffer, "buffer"); Contract.Ensures(Contract.Result<byte[]>() != null); using (var compressedDataStream = new MemoryStream(buffer)) { using (var decompressedDataStream = new MemoryStream()) { - using (var decompressingStream = new DeflateStream(compressedDataStream, CompressionMode.Decompress, true)) { + Stream decompressingStream = null; + try { + switch (method) { + case CompressionMethod.Deflate: + decompressingStream = new DeflateStream(compressedDataStream, CompressionMode.Decompress, true); + break; + case CompressionMethod.Gzip: + decompressingStream = new GZipStream(compressedDataStream, CompressionMode.Decompress, true); + break; + default: + Requires.InRange(false, "method"); + break; + } + decompressingStream.CopyTo(decompressedDataStream); + } finally { + if (decompressingStream != null) { + decompressingStream.Dispose(); + } } return decompressedDataStream.ToArray(); @@ -868,43 +977,6 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> - /// Compares to string values for ordinal equality in such a way that its execution time does not depend on how much of the value matches. - /// </summary> - /// <param name="value1">The first value.</param> - /// <param name="value2">The second value.</param> - /// <returns>A value indicating whether the two strings share ordinal equality.</returns> - /// <remarks> - /// In signature equality checks, a difference in execution time based on how many initial characters match MAY - /// be used as an attack to figure out the expected signature. It is therefore important to make a signature - /// equality check's execution time independent of how many characters match the expected value. - /// See http://codahale.com/a-lesson-in-timing-attacks/ for more information. - /// </remarks> - internal static bool EqualsConstantTime(string value1, string value2) { - // If exactly one value is null, they don't match. - if (value1 == null ^ value2 == null) { - return false; - } - - // If both values are null (since if one is at this point then they both are), it's a match. - if (value1 == null) { - return true; - } - - if (value1.Length != value2.Length) { - return false; - } - - // This looks like a pretty crazy way to compare values, but it provides a constant time equality check, - // and is more resistant to compiler optimizations than simply setting a boolean flag and returning the boolean after the loop. - int result = 0; - for (int i = 0; i < value1.Length; i++) { - result |= value1[i] ^ value2[i]; - } - - return result == 0; - } - - /// <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" /> @@ -1600,6 +1672,68 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Serializes the given message as a JSON string. + /// </summary> + /// <param name="message">The message to serialize.</param> + /// <param name="messageDescriptions">The cached message descriptions to use for reflection.</param> + /// <returns>A JSON string.</returns> + [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "This Dispose is safe.")] + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No apparent problem. False positive?")] + internal static string SerializeAsJson(IMessage message, MessageDescriptionCollection messageDescriptions) { + Requires.NotNull(message, "message"); + Requires.NotNull(messageDescriptions, "messageDescriptions"); + + var encoding = Encoding.UTF8; + var bytes = SerializeAsJsonBytes(message, messageDescriptions, encoding); + string json = encoding.GetString(bytes); + return json; + } + + /// <summary> + /// Serializes the given message as a JSON string. + /// </summary> + /// <param name="message">The message to serialize.</param> + /// <param name="messageDescriptions">The cached message descriptions to use for reflection.</param> + /// <param name="encoding">The encoding to use. Defaults to <see cref="Encoding.UTF8"/></param> + /// <returns>A JSON string.</returns> + [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "This Dispose is safe.")] + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No apparent problem. False positive?")] + internal static byte[] SerializeAsJsonBytes(IMessage message, MessageDescriptionCollection messageDescriptions, Encoding encoding = null) { + Requires.NotNull(message, "message"); + Requires.NotNull(messageDescriptions, "messageDescriptions"); + + encoding = encoding ?? Encoding.UTF8; + MessageDictionary messageDictionary = messageDescriptions.GetAccessor(message); + using (var memoryStream = new MemoryStream()) { + using (var jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(memoryStream, encoding)) { + MessageSerializer.Serialize(messageDictionary, jsonWriter); + jsonWriter.Flush(); + } + + return memoryStream.ToArray(); + } + } + + /// <summary> + /// Deserializes a JSON object into a message. + /// </summary> + /// <param name="jsonBytes">The buffer containing the JSON string.</param> + /// <param name="receivingMessage">The message to deserialize the object into.</param> + /// <param name="messageDescriptions">The cache of message descriptions.</param> + /// <param name="encoding">The encoding that the JSON bytes are in.</param> + internal static void DeserializeFromJson(byte[] jsonBytes, IMessage receivingMessage, MessageDescriptionCollection messageDescriptions, Encoding encoding = null) { + Requires.NotNull(jsonBytes, "jsonBytes"); + Requires.NotNull(receivingMessage, "receivingMessage"); + Requires.NotNull(messageDescriptions, "messageDescriptions"); + + encoding = encoding ?? Encoding.UTF8; + MessageDictionary messageDictionary = messageDescriptions.GetAccessor(receivingMessage); + using (var jsonReader = JsonReaderWriterFactory.CreateJsonReader(jsonBytes, 0, jsonBytes.Length, encoding, Channel.DefaultUntrustedXmlDictionaryReaderQuotas, null)) { + MessageSerializer.Deserialize(messageDictionary, jsonReader); + } + } + + /// <summary> /// Prepares what SHOULD be simply a string value for safe injection into Javascript /// by using appropriate character escaping. /// </summary> diff --git a/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponse.cs b/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponse.cs index 67eccce..9ef89e9 100644 --- a/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponse.cs +++ b/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponse.cs @@ -12,6 +12,7 @@ namespace DotNetOpenAuth.Messaging { using System.IO; using System.Net; using System.Net.Mime; + using System.ServiceModel.Web; using System.Text; using System.Threading; using System.Web; @@ -213,6 +214,23 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Submits this response to a WCF response context. Only available when no response body is included. + /// </summary> + /// <param name="responseContext">The response context to apply the response to.</param> + public virtual void Respond(OutgoingWebResponseContext responseContext) { + Requires.NotNull(responseContext, "responseContext"); + if (this.ResponseStream != null) { + throw new NotSupportedException(Strings.ResponseBodyNotSupported); + } + + responseContext.StatusCode = this.Status; + responseContext.SuppressEntityBody = true; + foreach (string header in this.Headers) { + responseContext.Headers[header] = this.Headers[header]; + } + } + + /// <summary> /// Automatically sends the appropriate response to the user agent. /// </summary> /// <param name="response">The response to set to this message.</param> diff --git a/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponseActionResult.cs b/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponseActionResult.cs index a5fe782..7691cc4 100644 --- a/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponseActionResult.cs +++ b/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponseActionResult.cs @@ -35,6 +35,11 @@ namespace DotNetOpenAuth.Messaging { /// <param name="context">The context in which to set the response.</param> public override void ExecuteResult(ControllerContext context) { this.response.Respond(context.HttpContext); + + // MVC likes to muck with our response. For example, when returning contrived 401 Unauthorized responses + // MVC will rewrite our response and turn it into a redirect, which breaks OAuth 2 authorization server token endpoints. + // It turns out we can prevent this unwanted behavior by flushing the response before returning from this method. + context.HttpContext.Response.Flush(); } } } diff --git a/src/DotNetOpenAuth.Core/Messaging/ProtocolException.cs b/src/DotNetOpenAuth.Core/Messaging/ProtocolException.cs index e26d15e..982e1c0 100644 --- a/src/DotNetOpenAuth.Core/Messaging/ProtocolException.cs +++ b/src/DotNetOpenAuth.Core/Messaging/ProtocolException.cs @@ -42,10 +42,10 @@ namespace DotNetOpenAuth.Messaging { /// such that it can be sent as a protocol message response to a remote caller. /// </summary> /// <param name="message">The human-readable exception message.</param> - /// <param name="faultedMessage">The message that was the cause of the exception. Must not be null.</param> - protected internal ProtocolException(string message, IProtocolMessage faultedMessage) - : base(message) { - Requires.NotNull(faultedMessage, "faultedMessage"); + /// <param name="faultedMessage">The message that was the cause of the exception. May be null.</param> + /// <param name="innerException">The inner exception to include.</param> + protected internal ProtocolException(string message, IProtocolMessage faultedMessage, Exception innerException = null) + : base(message, innerException) { this.FaultedMessage = faultedMessage; } diff --git a/src/DotNetOpenAuth.Core/Messaging/ProtocolFaultResponseException.cs b/src/DotNetOpenAuth.Core/Messaging/ProtocolFaultResponseException.cs new file mode 100644 index 0000000..c2dc34e --- /dev/null +++ b/src/DotNetOpenAuth.Core/Messaging/ProtocolFaultResponseException.cs @@ -0,0 +1,69 @@ +//----------------------------------------------------------------------- +// <copyright file="ProtocolFaultResponseException.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.Messaging { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + + /// <summary> + /// An exception to represent errors in the local or remote implementation of the protocol + /// that includes the response message that should be returned to the HTTP client to comply + /// with the protocol specification. + /// </summary> + public class ProtocolFaultResponseException : ProtocolException { + /// <summary> + /// The channel that produced the error response message, to be used in constructing the actual HTTP response. + /// </summary> + private readonly Channel channel; + + /// <summary> + /// Initializes a new instance of the <see cref="ProtocolFaultResponseException"/> class + /// such that it can be sent as a protocol message response to a remote caller. + /// </summary> + /// <param name="channel">The channel to use when encoding the response message.</param> + /// <param name="errorResponse">The message to send back to the HTTP client.</param> + /// <param name="faultedMessage">The message that was the cause of the exception. May be null.</param> + /// <param name="innerException">The inner exception.</param> + /// <param name="message">The message for the exception.</param> + protected internal ProtocolFaultResponseException(Channel channel, IDirectResponseProtocolMessage errorResponse, IProtocolMessage faultedMessage = null, Exception innerException = null, string message = null) + : base(message ?? (innerException != null ? innerException.Message : null), faultedMessage, innerException) { + Requires.NotNull(channel, "channel"); + Requires.NotNull(errorResponse, "errorResponse"); + this.channel = channel; + this.ErrorResponseMessage = errorResponse; + } + + /// <summary> + /// Initializes a new instance of the <see cref="ProtocolFaultResponseException"/> 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 ProtocolFaultResponseException( + System.Runtime.Serialization.SerializationInfo info, + System.Runtime.Serialization.StreamingContext context) + : base(info, context) { + throw new NotImplementedException(); + } + + /// <summary> + /// Gets the protocol message to send back to the client to report the error. + /// </summary> + public IDirectResponseProtocolMessage ErrorResponseMessage { get; private set; } + + /// <summary> + /// Creates the HTTP response to forward to the client to report the error. + /// </summary> + /// <returns>The HTTP response.</returns> + public OutgoingWebResponse CreateErrorResponse() { + var response = this.channel.PrepareResponse(this.ErrorResponseMessage); + return response; + } + } +} diff --git a/src/DotNetOpenAuth.Core/Messaging/StandardMessageFactoryChannel.cs b/src/DotNetOpenAuth.Core/Messaging/StandardMessageFactoryChannel.cs index 7a1d194..7ca5d45 100644 --- a/src/DotNetOpenAuth.Core/Messaging/StandardMessageFactoryChannel.cs +++ b/src/DotNetOpenAuth.Core/Messaging/StandardMessageFactoryChannel.cs @@ -31,7 +31,10 @@ namespace DotNetOpenAuth.Messaging { /// </summary> /// <param name="messageTypes">The message types that might be encountered.</param> /// <param name="versions">All the possible message versions that might be encountered.</param> - /// <param name="bindingElements">The binding elements to apply to the channel.</param> + /// <param name="bindingElements"> + /// The binding elements to use in sending and receiving messages. + /// The order they are provided is used for outgoing messgaes, and reversed for incoming messages. + /// </param> protected StandardMessageFactoryChannel(ICollection<Type> messageTypes, ICollection<Version> versions, params IChannelBindingElement[] bindingElements) : base(new StandardMessageFactory(), bindingElements) { Requires.NotNull(messageTypes, "messageTypes"); diff --git a/src/DotNetOpenAuth.Core/Messaging/UriStyleMessageFormatter.cs b/src/DotNetOpenAuth.Core/Messaging/UriStyleMessageFormatter.cs index 92b1928..242175e 100644 --- a/src/DotNetOpenAuth.Core/Messaging/UriStyleMessageFormatter.cs +++ b/src/DotNetOpenAuth.Core/Messaging/UriStyleMessageFormatter.cs @@ -20,7 +20,7 @@ namespace DotNetOpenAuth.Messaging { /// A serializer for <see cref="DataBag"/>-derived types /// </summary> /// <typeparam name="T">The DataBag-derived type that is to be serialized/deserialized.</typeparam> - internal class UriStyleMessageFormatter<T> : DataBagFormatterBase<T> where T : DataBag, new() { + internal class UriStyleMessageFormatter<T> : DataBagFormatterBase<T> where T : DataBag { /// <summary> /// Initializes a new instance of the <see cref="UriStyleMessageFormatter<T>"/> class. /// </summary> diff --git a/src/DotNetOpenAuth.Core/Properties/AssemblyInfo.cs b/src/DotNetOpenAuth.Core/Properties/AssemblyInfo.cs index e57b211..91d27f5 100644 --- a/src/DotNetOpenAuth.Core/Properties/AssemblyInfo.cs +++ b/src/DotNetOpenAuth.Core/Properties/AssemblyInfo.cs @@ -64,6 +64,7 @@ using System.Web.UI; [assembly: InternalsVisibleTo("DotNetOpenAuth.OpenIdOAuth, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.AuthorizationServer, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] +[assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.ClientAuthorization, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.ResourceServer, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.Client, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.Client.UI, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] @@ -88,6 +89,7 @@ using System.Web.UI; [assembly: InternalsVisibleTo("DotNetOpenAuth.OpenIdOAuth")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.AuthorizationServer")] +[assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.ClientAuthorization")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.ResourceServer")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.Client")] [assembly: InternalsVisibleTo("DotNetOpenAuth.AspNet.Test")] diff --git a/src/DotNetOpenAuth.Core/Requires.cs b/src/DotNetOpenAuth.Core/Requires.cs index 7a196a3..7d4d5be 100644 --- a/src/DotNetOpenAuth.Core/Requires.cs +++ b/src/DotNetOpenAuth.Core/Requires.cs @@ -43,14 +43,17 @@ namespace DotNetOpenAuth { /// </summary> /// <param name="value">The value.</param> /// <param name="parameterName">Name of the parameter.</param> + /// <returns>The validated value.</returns> #if !CLR4 [ContractArgumentValidator] #endif [Pure, DebuggerStepThrough] - internal static void NotNullOrEmpty(string value, string parameterName) { + internal static string NotNullOrEmpty(string value, string parameterName) { NotNull(value, parameterName); True(value.Length > 0, parameterName, Strings.EmptyStringNotAllowed); + Contract.Ensures(Contract.Result<string>() == value); Contract.EndContractBlock(); + return value; } /// <summary> diff --git a/src/DotNetOpenAuth.Core/Strings.Designer.cs b/src/DotNetOpenAuth.Core/Strings.Designer.cs index 21411a1..b0e66d2 100644 --- a/src/DotNetOpenAuth.Core/Strings.Designer.cs +++ b/src/DotNetOpenAuth.Core/Strings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:4.0.30319.17291 +// Runtime Version:4.0.30319.17622 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -106,6 +106,24 @@ namespace DotNetOpenAuth { } /// <summary> + /// Looks up a localized string similar to The property {0} must be set before this operation is allowed.. + /// </summary> + internal static string RequiredPropertyNotYetPreset { + get { + return ResourceManager.GetString("RequiredPropertyNotYetPreset", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to This object contains a response body, which is not supported.. + /// </summary> + internal static string ResponseBodyNotSupported { + get { + return ResourceManager.GetString("ResponseBodyNotSupported", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to No current HttpContext was detected, so an {0} instance must be explicitly provided or specified in the .config file. Call the constructor overload that takes an {0}.. /// </summary> internal static string StoreRequiredWhenNoHttpContextAvailable { diff --git a/src/DotNetOpenAuth.Core/Strings.resx b/src/DotNetOpenAuth.Core/Strings.resx index 1c69ef7..f4d61d1 100644 --- a/src/DotNetOpenAuth.Core/Strings.resx +++ b/src/DotNetOpenAuth.Core/Strings.resx @@ -112,10 +112,10 @@ <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> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.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> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <data name="ConfigurationTypeMustBePublic" xml:space="preserve"> <value>The configuration-specified type {0} must be public, and is not.</value> @@ -135,4 +135,10 @@ <data name="InvalidArgument" xml:space="preserve"> <value>The argument has an unexpected value.</value> </data> + <data name="RequiredPropertyNotYetPreset" xml:space="preserve"> + <value>The property {0} must be set before this operation is allowed.</value> + </data> + <data name="ResponseBodyNotSupported" xml:space="preserve"> + <value>This object contains a response body, which is not supported.</value> + </data> </root>
\ No newline at end of file |