summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/DotNetOpenAuth/Configuration/AssociationTypeCollection.cs3
-rw-r--r--src/DotNetOpenAuth/Configuration/HostNameOrRegexCollection.cs2
-rw-r--r--src/DotNetOpenAuth/Configuration/OpenIdElement.cs4
-rw-r--r--src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs8
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj8
-rw-r--r--src/DotNetOpenAuth/InfoCard/ClaimType.cs2
-rw-r--r--src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs6
-rw-r--r--src/DotNetOpenAuth/InfoCard/Token/Token.cs20
-rw-r--r--src/DotNetOpenAuth/InfoCard/Token/TokenUtility.cs3
-rw-r--r--src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs3
-rw-r--r--src/DotNetOpenAuth/Messaging/Channel.cs8
-rw-r--r--src/DotNetOpenAuth/Messaging/ErrorUtilities.cs6
-rw-r--r--src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs50
-rw-r--r--src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs37
-rw-r--r--src/DotNetOpenAuth/Messaging/IncomingWebResponseContract.cs54
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs11
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingStrings.resx3
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingUtilities.cs1
-rw-r--r--src/DotNetOpenAuth/Messaging/NetworkDirectWebResponse.cs5
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs1
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs4
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs26
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs19
-rw-r--r--src/DotNetOpenAuth/OpenId/Association.cs63
-rw-r--r--src/DotNetOpenAuth/OpenId/AssociationContract.cs65
-rw-r--r--src/DotNetOpenAuth/OpenId/Associations.cs15
-rw-r--r--src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs13
-rw-r--r--src/DotNetOpenAuth/OpenId/IAssociationStore.cs64
-rw-r--r--src/DotNetOpenAuth/OpenId/Protocol.cs28
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs16
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs6
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs20
-rw-r--r--src/DotNetOpenAuth/OpenId/XriIdentifier.cs1
33 files changed, 409 insertions, 166 deletions
diff --git a/src/DotNetOpenAuth/Configuration/AssociationTypeCollection.cs b/src/DotNetOpenAuth/Configuration/AssociationTypeCollection.cs
index 56c7389..881fcdb 100644
--- a/src/DotNetOpenAuth/Configuration/AssociationTypeCollection.cs
+++ b/src/DotNetOpenAuth/Configuration/AssociationTypeCollection.cs
@@ -54,8 +54,7 @@ namespace DotNetOpenAuth.Configuration {
/// An <see cref="T:System.Object"/> that acts as the key for the specified <see cref="T:System.Configuration.ConfigurationElement"/>.
/// </returns>
protected override object GetElementKey(ConfigurationElement element) {
- Contract.Assume(element != null); // this should be Contract.Requires in base class.
- return ((AssociationTypeElement)element).AssociationType;
+ return ((AssociationTypeElement)element).AssociationType ?? string.Empty;
}
}
}
diff --git a/src/DotNetOpenAuth/Configuration/HostNameOrRegexCollection.cs b/src/DotNetOpenAuth/Configuration/HostNameOrRegexCollection.cs
index 88a1615..c7d963b 100644
--- a/src/DotNetOpenAuth/Configuration/HostNameOrRegexCollection.cs
+++ b/src/DotNetOpenAuth/Configuration/HostNameOrRegexCollection.cs
@@ -64,7 +64,7 @@ namespace DotNetOpenAuth.Configuration {
/// </returns>
protected override object GetElementKey(ConfigurationElement element) {
Contract.Assume(element != null); // this should be Contract.Requires in base class.
- return ((HostNameElement)element).Name;
+ return ((HostNameElement)element).Name ?? string.Empty;
}
}
}
diff --git a/src/DotNetOpenAuth/Configuration/OpenIdElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdElement.cs
index dd48175..b272024 100644
--- a/src/DotNetOpenAuth/Configuration/OpenIdElement.cs
+++ b/src/DotNetOpenAuth/Configuration/OpenIdElement.cs
@@ -55,7 +55,9 @@ namespace DotNetOpenAuth.Configuration {
internal TimeSpan MaxAuthenticationTime {
get {
Contract.Ensures(Contract.Result<TimeSpan>() > TimeSpan.Zero);
- return (TimeSpan)this[MaxAuthenticationTimePropertyName];
+ TimeSpan result = (TimeSpan)this[MaxAuthenticationTimePropertyName];
+ Contract.Assume(result > TimeSpan.Zero); // our PositiveTimeSpanValidator should take care of this
+ return result;
}
set {
diff --git a/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs b/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs
index 022ef40..20cd332 100644
--- a/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs
+++ b/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs
@@ -9,13 +9,15 @@ namespace DotNetOpenAuth.Configuration {
using System.Configuration;
using System.Reflection;
using DotNetOpenAuth.Messaging;
+ using System.Diagnostics.Contracts;
/// <summary>
/// Represents an element in a .config file that allows the user to provide a @type attribute specifying
/// the full type that provides some service used by this library.
/// </summary>
/// <typeparam name="T">A constraint on the type the user may provide.</typeparam>
- internal class TypeConfigurationElement<T> : ConfigurationElement {
+ internal class TypeConfigurationElement<T> : ConfigurationElement
+ where T : class {
/// <summary>
/// The name of the attribute whose value is the full name of the type the user is specifying.
/// </summary>
@@ -51,6 +53,8 @@ namespace DotNetOpenAuth.Configuration {
/// <param name="defaultValue">The value to return if no type is given in the .config file.</param>
/// <returns>The newly instantiated type.</returns>
public T CreateInstance(T defaultValue) {
+ Contract.Ensures(Contract.Result<T>() != null || Contract.Result<T>() == defaultValue);
+
return this.CreateInstance(defaultValue, false);
}
@@ -61,6 +65,8 @@ namespace DotNetOpenAuth.Configuration {
/// <param name="allowInternals">if set to <c>true</c> then internal types may be instantiated.</param>
/// <returns>The newly instantiated type.</returns>
public T CreateInstance(T defaultValue, bool allowInternals) {
+ Contract.Ensures(Contract.Result<T>() != null || Contract.Result<T>() == defaultValue);
+
if (this.CustomType != null) {
if (!allowInternals) {
// Although .NET will usually prevent our instantiating non-public types,
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
index 859b0c6..ec37b14 100644
--- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj
+++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
@@ -110,14 +110,14 @@
</CodeContractsLibPaths>
<CodeContractsPlatformPath>
</CodeContractsPlatformPath>
- <CodeContractsExtraAnalysisOptions>
- </CodeContractsExtraAnalysisOptions>
+ <CodeContractsExtraAnalysisOptions>-maxpathsize 15</CodeContractsExtraAnalysisOptions>
<CodeContractsBaseLineFile>
</CodeContractsBaseLineFile>
<CodeContractsUseBaseLine>False</CodeContractsUseBaseLine>
<CodeContractsRunInBackground>True</CodeContractsRunInBackground>
<CodeContractsShowSquigglies>True</CodeContractsShowSquigglies>
<RunCodeAnalysis>true</RunCodeAnalysis>
+ <CodeContractsArithmeticObligations>False</CodeContractsArithmeticObligations>
</PropertyGroup>
<ItemGroup>
<Reference Include="log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=1b44e1d426115821, processorArchitecture=MSIL">
@@ -215,6 +215,7 @@
<Compile Include="Messaging\EmptyList.cs" />
<Compile Include="Messaging\ErrorUtilities.cs" />
<Compile Include="Messaging\IMessageWithEvents.cs" />
+ <Compile Include="Messaging\IncomingWebResponseContract.cs" />
<Compile Include="Messaging\IProtocolMessageWithExtensions.cs" />
<Compile Include="Messaging\InternalErrorException.cs" />
<Compile Include="Messaging\KeyedCollectionDelegate.cs" />
@@ -318,6 +319,7 @@
<Compile Include="OpenId\ChannelElements\OpenIdMessageFactory.cs" />
<Compile Include="OpenId\ChannelElements\ReturnToSignatureBindingElement.cs" />
<Compile Include="OpenId\ChannelElements\SkipSecurityBindingElement.cs" />
+ <Compile Include="OpenId\AssociationContract.cs" />
<Compile Include="OpenId\Extensions\AliasManager.cs" />
<Compile Include="OpenId\Extensions\AttributeExchange\AttributeRequest.cs" />
<Compile Include="OpenId\Extensions\AttributeExchange\AttributeValues.cs" />
@@ -538,4 +540,4 @@
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\tools\DotNetOpenAuth.Versioning.targets" />
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/DotNetOpenAuth/InfoCard/ClaimType.cs b/src/DotNetOpenAuth/InfoCard/ClaimType.cs
index 96a3d32..f9c4917 100644
--- a/src/DotNetOpenAuth/InfoCard/ClaimType.cs
+++ b/src/DotNetOpenAuth/InfoCard/ClaimType.cs
@@ -7,9 +7,9 @@
namespace DotNetOpenAuth.InfoCard {
using System;
using System.ComponentModel;
+ using System.Diagnostics.Contracts;
using System.IdentityModel.Claims;
using System.Web.UI;
-using System.Diagnostics.Contracts;
/// <summary>
/// Description of a claim that is requested or required in a submitted Information Card.
diff --git a/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs b/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs
index b787300..6e54746 100644
--- a/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs
+++ b/src/DotNetOpenAuth/InfoCard/InfoCardSelector.cs
@@ -583,6 +583,8 @@ namespace DotNetOpenAuth.InfoCard {
/// <returns>A control that renders to the &lt;object&gt; tag.</returns>
[Pure]
private Control CreateInfoCardSelectorObject() {
+ Contract.Ensures(Contract.Result<Control>() != null);
+
HtmlGenericControl cardSpaceControl = new HtmlGenericControl(HtmlTextWriterTag.Object.ToString());
cardSpaceControl.Attributes.Add(HtmlTextWriterAttribute.Type.ToString(), "application/x-informationcard");
cardSpaceControl.Attributes.Add(HtmlTextWriterAttribute.Id.ToString(), this.ClientID + "_cs");
@@ -649,10 +651,10 @@ namespace DotNetOpenAuth.InfoCard {
string[] requiredClaimsArray = requiredClaims.ToArray();
string[] optionalClaimsArray = optionalClaims.ToArray();
- Contract.Assume(requiredClaimsArray != null);
- Contract.Assume(optionalClaimsArray != null);
required = string.Join(" ", requiredClaimsArray);
optional = string.Join(" ", optionalClaimsArray);
+ Contract.Assume(required != null);
+ Contract.Assume(optional != null);
}
/// <summary>
diff --git a/src/DotNetOpenAuth/InfoCard/Token/Token.cs b/src/DotNetOpenAuth/InfoCard/Token/Token.cs
index f07c555..f66f4ce 100644
--- a/src/DotNetOpenAuth/InfoCard/Token/Token.cs
+++ b/src/DotNetOpenAuth/InfoCard/Token/Token.cs
@@ -42,19 +42,21 @@ namespace DotNetOpenAuth.InfoCard {
/// <param name="decryptor">The decryptor to use to decrypt the token, if necessary..</param>
/// <exception cref="InformationCardException">Thrown for any problem decoding or decrypting the token.</exception>
private Token(string tokenXml, Uri audience, TokenDecryptor decryptor) {
- Contract.Requires(tokenXml != null && tokenXml.Length > 0);
+ Contract.Requires(!String.IsNullOrEmpty(tokenXml));
Contract.Requires(decryptor != null || !IsEncrypted(tokenXml));
- ErrorUtilities.VerifyNonZeroLength(tokenXml, "tokenXml");
+ Contract.Ensures(this.AuthorizationContext != null);
byte[] decryptedBytes;
string decryptedString;
using (XmlReader tokenReader = XmlReader.Create(new StringReader(tokenXml))) {
+ Contract.Assume(tokenReader != null); // BCL contract should say XmlReader.Create result != null
if (IsEncrypted(tokenReader)) {
Logger.InfoCard.DebugFormat("Incoming SAML token, before decryption: {0}", tokenXml);
ErrorUtilities.VerifyArgumentNotNull(decryptor, "decryptor");
decryptedBytes = decryptor.DecryptToken(tokenReader);
decryptedString = Encoding.UTF8.GetString(decryptedBytes);
+ Contract.Assume(decryptedString != null); // BCL contracts should be enhanced here
} else {
decryptedBytes = Encoding.UTF8.GetBytes(tokenXml);
decryptedString = tokenXml;
@@ -106,7 +108,7 @@ namespace DotNetOpenAuth.InfoCard {
/// </summary>
public string SiteSpecificId {
get {
- Contract.Requires(this.Claims.ContainsKey(ClaimTypes.PPID));
+ Contract.Requires(this.Claims.ContainsKey(ClaimTypes.PPID) && !string.IsNullOrEmpty(this.Claims[ClaimTypes.PPID]));
ErrorUtilities.VerifyOperation(this.Claims.ContainsKey(ClaimTypes.PPID), InfoCardStrings.PpidClaimRequired);
return TokenUtility.CalculateSiteSpecificID(this.Claims[ClaimTypes.PPID]);
}
@@ -197,6 +199,18 @@ namespace DotNetOpenAuth.InfoCard {
}
}
+#if CONTRACTS_FULL
+ /// <summary>
+ /// Verifies conditions that should be true for any valid state of this object.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ protected void ObjectInvariant()
+ {
+ Contract.Invariant(this.AuthorizationContext != null);
+ }
+#endif
+
/// <summary>
/// Determines whether the specified token XML is encrypted.
/// </summary>
diff --git a/src/DotNetOpenAuth/InfoCard/Token/TokenUtility.cs b/src/DotNetOpenAuth/InfoCard/Token/TokenUtility.cs
index 85b951d..e58f051 100644
--- a/src/DotNetOpenAuth/InfoCard/Token/TokenUtility.cs
+++ b/src/DotNetOpenAuth/InfoCard/Token/TokenUtility.cs
@@ -48,6 +48,8 @@ namespace DotNetOpenAuth.InfoCard {
/// The authorization context carried by the token.
/// </returns>
internal static AuthorizationContext AuthenticateToken(XmlReader reader, Uri audience) {
+ Contract.Ensures(Contract.Result<AuthorizationContext>() != null);
+
// Extensibility Point:
// in order to accept different token types, you would need to add additional
// code to create an authenticationcontext from the security token.
@@ -218,7 +220,6 @@ namespace DotNetOpenAuth.InfoCard {
/// <returns>A string containing the XXX-XXXX-XXX cosmetic value.</returns>
internal static string CalculateSiteSpecificID(string ppid) {
Contract.Requires(ppid != null);
- ErrorUtilities.VerifyArgumentNotNull(ppid, "ppid");
Contract.Ensures(Contract.Result<string>() != null && Contract.Result<string>().Length > 0);
int callSignChars = 10;
diff --git a/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs b/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs
index 0a24cea..e586ca8 100644
--- a/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs
+++ b/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs
@@ -37,6 +37,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="maximumBytesToRead">The maximum bytes to read.</param>
internal CachedDirectWebResponse(Uri requestUri, HttpWebResponse response, int maximumBytesToRead)
: base(requestUri, response) {
+ Contract.RequiresAlways(requestUri != null);
+ Contract.RequiresAlways(response != null);
this.responseStream = CacheNetworkStreamAndClose(response, maximumBytesToRead);
// BUGBUG: if the response was exactly maximumBytesToRead, we'll incorrectly believe it was truncated.
@@ -55,6 +57,7 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="responseStream">The response stream.</param>
internal CachedDirectWebResponse(Uri requestUri, Uri responseUri, WebHeaderCollection headers, HttpStatusCode statusCode, string contentType, string contentEncoding, MemoryStream responseStream)
: base(requestUri, responseUri, headers, statusCode, contentType, contentEncoding) {
+ Contract.RequiresAlways(requestUri != null);
Contract.Requires(responseStream != null);
this.responseStream = responseStream;
}
diff --git a/src/DotNetOpenAuth/Messaging/Channel.cs b/src/DotNetOpenAuth/Messaging/Channel.cs
index 0813d17..e2b7adc 100644
--- a/src/DotNetOpenAuth/Messaging/Channel.cs
+++ b/src/DotNetOpenAuth/Messaging/Channel.cs
@@ -441,6 +441,10 @@ namespace DotNetOpenAuth.Messaging {
[SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Costly call should not be a property.")]
protected internal virtual HttpRequestInfo GetRequestFromContext() {
Contract.Ensures(Contract.Result<HttpRequestInfo>() != null);
+ Contract.Ensures(Contract.Result<HttpRequestInfo>().Url != null);
+ Contract.Ensures(Contract.Result<HttpRequestInfo>().RawUrl != null);
+ Contract.Ensures(Contract.Result<HttpRequestInfo>().UrlBeforeRewriting != null);
+
ErrorUtilities.VerifyHttpContext();
return new HttpRequestInfo(HttpContext.Current.Request);
@@ -591,7 +595,9 @@ namespace DotNetOpenAuth.Messaging {
Contract.Ensures(Contract.Result<OutgoingWebResponse>() != null);
ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ Contract.Assert(message != null && message.Recipient != null);
var messageAccessor = this.MessageDescriptions.GetAccessor(message);
+ Contract.Assert(message != null && message.Recipient != null);
var fields = messageAccessor.Serialize();
// First try creating a 301 redirect, and fallback to a form POST
@@ -777,7 +783,7 @@ namespace DotNetOpenAuth.Messaging {
/// </remarks>
protected virtual HttpWebRequest InitializeRequestAsGet(IDirectedProtocolMessage requestMessage) {
Contract.Requires(requestMessage != null);
- ErrorUtilities.VerifyArgumentNotNull(requestMessage, "requestMessage");
+ Contract.Requires(requestMessage.Recipient != null);
var messageAccessor = this.MessageDescriptions.GetAccessor(requestMessage);
var fields = messageAccessor.Serialize();
diff --git a/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs b/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs
index 1c27d6c..9f2a514 100644
--- a/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs
+++ b/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs
@@ -350,7 +350,8 @@ namespace DotNetOpenAuth.Messaging {
/// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> is null.</exception>
[Pure]
internal static void VerifyArgumentNotNull(object value, string paramName) {
- Contract.Requires(value != null);
+ Contract.Ensures(value != null);
+ Contract.EnsuresOnThrow<ArgumentNullException>(value == null);
if (value == null) {
throw new ArgumentNullException(paramName);
}
@@ -379,7 +380,8 @@ namespace DotNetOpenAuth.Messaging {
[Pure]
internal static void VerifyHttpContext() {
Contract.Ensures(HttpContext.Current != null);
- ErrorUtilities.VerifyOperation(HttpContext.Current != null, MessagingStrings.HttpContextRequired);
+ Contract.Ensures(HttpContext.Current.Request != null);
+ ErrorUtilities.VerifyOperation(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
}
}
}
diff --git a/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs b/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs
index e84a097..30b4260 100644
--- a/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs
+++ b/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs
@@ -49,7 +49,16 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="request">The ASP.NET structure to copy from.</param>
public HttpRequestInfo(HttpRequest request) {
- Contract.Requires(request != null);
+ Contract.RequiresAlways(request != null);
+ Contract.Ensures(this.HttpMethod == request.HttpMethod);
+ Contract.Ensures(this.Url == request.Url);
+ Contract.Ensures(this.RawUrl == request.RawUrl);
+ Contract.Ensures(this.UrlBeforeRewriting != null);
+ Contract.Ensures(this.Headers != null);
+ Contract.Ensures(this.InputStream == request.InputStream);
+ Contract.Ensures(this.form == request.Form);
+ Contract.Ensures(this.queryString == request.QueryString);
+
ErrorUtilities.VerifyArgumentNotNull(request, "request");
this.HttpMethod = request.HttpMethod;
@@ -76,14 +85,10 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="headers">Headers in the HTTP request.</param>
/// <param name="inputStream">The entity stream, if any. (POST requests typically have these). Use <c>null</c> for GET requests.</param>
public HttpRequestInfo(string httpMethod, Uri requestUrl, string rawUrl, WebHeaderCollection headers, Stream inputStream) {
- Contract.Requires(!string.IsNullOrEmpty(httpMethod));
- Contract.Requires(requestUrl != null);
- Contract.Requires(rawUrl != null);
- Contract.Requires(headers != null);
- ErrorUtilities.VerifyNonZeroLength(httpMethod, "httpMethod");
- ErrorUtilities.VerifyArgumentNotNull(requestUrl, "requestUrl");
- ErrorUtilities.VerifyArgumentNotNull(rawUrl, "rawUrl");
- ErrorUtilities.VerifyArgumentNotNull(headers, "headers");
+ Contract.RequiresAlways(!string.IsNullOrEmpty(httpMethod));
+ Contract.RequiresAlways(requestUrl != null);
+ Contract.RequiresAlways(rawUrl != null);
+ Contract.RequiresAlways(headers != null);
this.HttpMethod = httpMethod;
this.Url = requestUrl;
@@ -97,8 +102,7 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="listenerRequest">Details on the incoming HTTP request.</param>
public HttpRequestInfo(HttpListenerRequest listenerRequest) {
- Contract.Requires(listenerRequest != null);
- ErrorUtilities.VerifyArgumentNotNull(listenerRequest, "listenerRequest");
+ Contract.RequiresAlways(listenerRequest != null);
this.HttpMethod = listenerRequest.HttpMethod;
this.Url = listenerRequest.Url;
@@ -117,10 +121,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="request">The WCF incoming request structure to get the HTTP information from.</param>
/// <param name="requestUri">The URI of the service endpoint.</param>
public HttpRequestInfo(HttpRequestMessageProperty request, Uri requestUri) {
- Contract.Requires(request != null);
- Contract.Requires(requestUri != null);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
- ErrorUtilities.VerifyArgumentNotNull(requestUri, "requestUri");
+ Contract.RequiresAlways(request != null);
+ Contract.RequiresAlways(requestUri != null);
this.HttpMethod = request.Method;
this.Headers = request.Headers;
@@ -132,6 +134,9 @@ namespace DotNetOpenAuth.Messaging {
/// Initializes a new instance of the <see cref="HttpRequestInfo"/> class.
/// </summary>
internal HttpRequestInfo() {
+ Contract.Ensures(this.HttpMethod == "GET");
+ Contract.Ensures(this.Headers != null);
+
this.HttpMethod = "GET";
this.Headers = new WebHeaderCollection();
}
@@ -141,8 +146,7 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
/// <param name="request">The HttpWebRequest (that was never used) to copy from.</param>
internal HttpRequestInfo(WebRequest request) {
- Contract.Requires(request != null);
- ErrorUtilities.VerifyArgumentNotNull(request, "request");
+ Contract.RequiresAlways(request != null);
this.HttpMethod = request.Method;
this.Url = request.RequestUri;
@@ -194,6 +198,8 @@ namespace DotNetOpenAuth.Messaging {
/// </summary>
internal Uri UrlBeforeRewriting {
get {
+ Contract.Ensures(Contract.Result<Uri>() != null || this.Url == null || this.RawUrl == null);
+
if (this.Url == null || this.RawUrl == null) {
return null;
}
@@ -314,6 +320,16 @@ namespace DotNetOpenAuth.Messaging {
return query;
}
+#if CONTRACTS_FULL
+ /// <summary>
+ /// Verifies conditions that should be true for any valid state of this object.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ protected void ObjectInvariant() {
+ }
+#endif
+
/// <summary>
/// Gets the public facing URL for the given incoming HTTP request.
/// </summary>
diff --git a/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs b/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs
index de1e700..8f169c3 100644
--- a/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs
+++ b/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs
@@ -6,7 +6,6 @@
namespace DotNetOpenAuth.Messaging {
using System;
- using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
@@ -189,40 +188,4 @@ namespace DotNetOpenAuth.Messaging {
}
}
}
-
- [ContractClassFor(typeof(IncomingWebResponse))]
- internal abstract class IncomingWebResponseContract : IncomingWebResponse {
- public override Stream ResponseStream {
- get { throw new NotImplementedException(); }
- }
-
- /// <summary>
- /// Creates a text reader for the response stream.
- /// </summary>
- /// <returns>
- /// The text reader, initialized for the proper encoding.
- /// </returns>
- public override StreamReader GetResponseReader() {
- Contract.Ensures(Contract.Result<StreamReader>() != null);
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Gets an offline snapshot version of this instance.
- /// </summary>
- /// <param name="maximumBytesToCache">The maximum bytes from the response stream to cache.</param>
- /// <returns>A snapshot version of this instance.</returns>
- /// <remarks>
- /// If this instance is a <see cref="NetworkDirectWebResponse"/> creating a snapshot
- /// will automatically close and dispose of the underlying response stream.
- /// If this instance is a <see cref="CachedDirectWebResponse"/>, the result will
- /// be the self same instance.
- /// </remarks>
- internal override CachedDirectWebResponse GetSnapshot(int maximumBytesToCache) {
- Contract.Requires(maximumBytesToCache >= 0);
- Contract.Ensures(Contract.Result<CachedDirectWebResponse>() != null);
- throw new NotImplementedException();
- }
- }
-
}
diff --git a/src/DotNetOpenAuth/Messaging/IncomingWebResponseContract.cs b/src/DotNetOpenAuth/Messaging/IncomingWebResponseContract.cs
new file mode 100644
index 0000000..04d3b38
--- /dev/null
+++ b/src/DotNetOpenAuth/Messaging/IncomingWebResponseContract.cs
@@ -0,0 +1,54 @@
+//-----------------------------------------------------------------------
+// <copyright file="IncomingWebResponseContract.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Diagnostics.Contracts;
+ using System.IO;
+
+ /// <summary>
+ /// Code contract for the <see cref="IncomingWebResponse"/> class.
+ /// </summary>
+ [ContractClassFor(typeof(IncomingWebResponse))]
+ internal abstract class IncomingWebResponseContract : IncomingWebResponse {
+ /// <summary>
+ /// Gets the body of the HTTP response.
+ /// </summary>
+ /// <value></value>
+ public override Stream ResponseStream {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// Creates a text reader for the response stream.
+ /// </summary>
+ /// <returns>
+ /// The text reader, initialized for the proper encoding.
+ /// </returns>
+ public override StreamReader GetResponseReader() {
+ Contract.Ensures(Contract.Result<StreamReader>() != null);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Gets an offline snapshot version of this instance.
+ /// </summary>
+ /// <param name="maximumBytesToCache">The maximum bytes from the response stream to cache.</param>
+ /// <returns>A snapshot version of this instance.</returns>
+ /// <remarks>
+ /// If this instance is a <see cref="NetworkDirectWebResponse"/> creating a snapshot
+ /// will automatically close and dispose of the underlying response stream.
+ /// If this instance is a <see cref="CachedDirectWebResponse"/>, the result will
+ /// be the self same instance.
+ /// </remarks>
+ internal override CachedDirectWebResponse GetSnapshot(int maximumBytesToCache) {
+ Contract.Requires(maximumBytesToCache >= 0);
+ Contract.Requires(this.RequestUri != null);
+ Contract.Ensures(Contract.Result<CachedDirectWebResponse>() != null);
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs
index fb8620b..daf3def 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
-// Runtime Version:2.0.50727.3521
+// Runtime Version:2.0.50727.3053
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -304,6 +304,15 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Looks up a localized string similar to A non-empty string was expected..
+ /// </summary>
+ internal static string NonEmptyStringExpected {
+ get {
+ return ResourceManager.GetString("NonEmptyStringExpected", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to A message response is already queued for sending in the response stream..
/// </summary>
internal static string QueuedMessageResponseAlreadyExists {
diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx
index 0a6f6a5..7a0ff57 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx
+++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx
@@ -288,4 +288,7 @@
<data name="UnexpectedHttpStatusCode" xml:space="preserve">
<value>Expected direct response to use HTTP status code {0} but was {1} instead.</value>
</data>
+ <data name="NonEmptyStringExpected" xml:space="preserve">
+ <value>A non-empty string was expected.</value>
+ </data>
</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
index fe1e4e8..f9798f8 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
@@ -604,6 +604,7 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="comparer">A comparison function to compare keys.</param>
/// <returns>An System.Linq.IOrderedEnumerable&lt;TElement&gt; whose elements are sorted according to a key.</returns>
internal static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Comparison<TKey> comparer) {
+ Contract.Ensures(Contract.Result<IOrderedEnumerable<TSource>>() != null);
return System.Linq.Enumerable.OrderBy<TSource, TKey>(source, keySelector, new ComparisonHelper<TKey>(comparer));
}
diff --git a/src/DotNetOpenAuth/Messaging/NetworkDirectWebResponse.cs b/src/DotNetOpenAuth/Messaging/NetworkDirectWebResponse.cs
index 3bbec6e..136b0d0 100644
--- a/src/DotNetOpenAuth/Messaging/NetworkDirectWebResponse.cs
+++ b/src/DotNetOpenAuth/Messaging/NetworkDirectWebResponse.cs
@@ -7,10 +7,10 @@
namespace DotNetOpenAuth.Messaging {
using System;
using System.Diagnostics;
+ using System.Diagnostics.Contracts;
using System.IO;
using System.Net;
using System.Text;
-using System.Diagnostics.Contracts;
/// <summary>
/// A live network HTTP response
@@ -42,6 +42,8 @@ using System.Diagnostics.Contracts;
/// <param name="response">The response.</param>
internal NetworkDirectWebResponse(Uri requestUri, HttpWebResponse response)
: base(requestUri, response) {
+ Contract.RequiresAlways(requestUri != null);
+ Contract.RequiresAlways(response != null);
this.httpWebResponse = response;
this.responseStream = response.GetResponseStream();
}
@@ -84,6 +86,7 @@ using System.Diagnostics.Contracts;
/// </remarks>
internal override CachedDirectWebResponse GetSnapshot(int maximumBytesToCache) {
ErrorUtilities.VerifyOperation(!this.streamReadBegun, "Network stream reading has already begun.");
+
this.streamReadBegun = true;
var result = new CachedDirectWebResponse(this.RequestUri, this.httpWebResponse, maximumBytesToCache);
this.Dispose();
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs
index 5cb7877..60ce7fa 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs
@@ -70,6 +70,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// </summary>
/// <param name="message">The message the dictionary should provide access to.</param>
/// <returns>The dictionary accessor to the message</returns>
+ [Pure]
internal MessageDictionary GetDictionary(IMessage message) {
Contract.Requires(message != null);
Contract.Ensures(Contract.Result<MessageDictionary>() != null);
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs
index 9baa67d..ff93df2 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs
@@ -32,6 +32,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <param name="messageType">A type that implements <see cref="IMessage"/>.</param>
/// <param name="messageVersion">The protocol version of the message.</param>
/// <returns>A <see cref="MessageDescription"/> instance.</returns>
+ [Pure]
internal MessageDescription Get(Type messageType, Version messageVersion) {
Contract.Requires(messageType != null && typeof(IMessage).IsAssignableFrom(messageType));
Contract.Requires(messageVersion != null);
@@ -50,6 +51,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
}
}
+ Contract.Assume(result != null); // The reflectedMessageTypes dictionary should never have null values.
return result;
}
@@ -61,6 +63,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <returns>
/// A <see cref="MessageDescription"/> instance.
/// </returns>
+ [Pure]
internal MessageDescription Get(IMessage message) {
Contract.Requires(message != null);
Contract.Ensures(Contract.Result<MessageDescription>() != null);
@@ -72,6 +75,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// </summary>
/// <param name="message">The message.</param>
/// <returns>The dictionary.</returns>
+ [Pure]
internal MessageDictionary GetAccessor(IMessage message) {
Contract.Requires(message != null);
ErrorUtilities.VerifyArgumentNotNull(message, "message");
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
index ec38867..73b62ef 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
/// <summary>
@@ -16,23 +17,24 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// provides access to both well-defined message properties and "extra"
/// name/value pairs that have no properties associated with them.
/// </summary>
- [ContractVerification(true)]
+ [ContractVerification(false)]
internal class MessageDictionary : IDictionary<string, string> {
/// <summary>
/// The <see cref="IMessage"/> instance manipulated by this dictionary.
/// </summary>
- private IMessage message;
+ private readonly IMessage message;
/// <summary>
/// The <see cref="MessageDescription"/> instance that describes the message type.
/// </summary>
- private MessageDescription description;
+ private readonly MessageDescription description;
/// <summary>
/// Initializes a new instance of the <see cref="MessageDictionary"/> class.
/// </summary>
/// <param name="message">The message instance whose values will be manipulated by this dictionary.</param>
/// <param name="description">The message description.</param>
+ [Pure]
internal MessageDictionary(IMessage message, MessageDescription description) {
Contract.Requires(message != null);
Contract.Requires(description != null);
@@ -201,9 +203,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// Thrown if <paramref name="value"/> is null.
/// </exception>
public void Add(string key, string value) {
- if (value == null) {
- throw new ArgumentNullException("value");
- }
+ ErrorUtilities.VerifyArgumentNotNull(value, "value");
MessagePart part;
if (this.description.Mapping.TryGetValue(key, out part)) {
@@ -371,6 +371,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// </summary>
/// <returns>The generated dictionary.</returns>
public IDictionary<string, string> Serialize() {
+ Contract.Ensures(Contract.Result<IDictionary<string, string>>() != null);
return this.Serializer.Serialize(this);
}
@@ -379,7 +380,20 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// </summary>
/// <param name="fields">The data to load into the message.</param>
public void Deserialize(IDictionary<string, string> fields) {
+ Contract.Requires(fields != null);
this.Serializer.Deserialize(fields, this);
}
+
+#if CONTRACTS_FULL
+ /// <summary>
+ /// Verifies conditions that should be true for any valid state of this object.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ protected void ObjectInvariant() {
+ Contract.Ensures(this.message != null);
+ Contract.Ensures(this.description != null);
+ }
+#endif
}
}
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
index f922cc8..8cf86c7 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
@@ -61,12 +61,25 @@ namespace DotNetOpenAuth.Messaging.Reflection {
[SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "By design.")]
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Much more efficient initialization when we can call methods.")]
static MessagePart() {
- Map<Uri>(uri => uri.AbsoluteUri, str => new Uri(str));
+ Func<string, Uri> safeUri = str => {
+ Contract.Assume(str != null);
+ return new Uri(str);
+ };
+ Func<string, bool> safeBool = str => {
+ Contract.Assume(str != null);
+ return bool.Parse(str);
+ };
+ Func<string, Identifier> safeIdentfier = str => {
+ Contract.Assume(str != null);
+ ErrorUtilities.VerifyFormat(str.Length > 0, MessagingStrings.NonEmptyStringExpected);
+ return Identifier.Parse(str);
+ };
+ Map<Uri>(uri => uri.AbsoluteUri, safeUri);
Map<DateTime>(dt => XmlConvert.ToString(dt, XmlDateTimeSerializationMode.Utc), str => XmlConvert.ToDateTime(str, XmlDateTimeSerializationMode.Utc));
Map<byte[]>(bytes => Convert.ToBase64String(bytes), str => Convert.FromBase64String(str));
Map<Realm>(realm => realm.ToString(), str => new Realm(str));
- Map<Identifier>(id => id.ToString(), str => Identifier.Parse(str));
- Map<bool>(value => value.ToString().ToLowerInvariant(), str => bool.Parse(str));
+ Map<Identifier>(id => id.ToString(), safeIdentfier);
+ Map<bool>(value => value.ToString().ToLowerInvariant(), safeBool);
}
/// <summary>
diff --git a/src/DotNetOpenAuth/OpenId/Association.cs b/src/DotNetOpenAuth/OpenId/Association.cs
index 7514cf9..82890c9 100644
--- a/src/DotNetOpenAuth/OpenId/Association.cs
+++ b/src/DotNetOpenAuth/OpenId/Association.cs
@@ -33,10 +33,14 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="handle">The handle.</param>
/// <param name="secret">The secret.</param>
/// <param name="totalLifeLength">How long the association will be useful.</param>
- /// <param name="issued">When this association was originally issued by the Provider.</param>
+ /// <param name="issued">The UTC time of when this association was originally issued by the Provider.</param>
protected Association(string handle, byte[] secret, TimeSpan totalLifeLength, DateTime issued) {
Contract.RequiresAlways(!string.IsNullOrEmpty(handle));
Contract.RequiresAlways(secret != null);
+ Contract.RequiresAlways(totalLifeLength > TimeSpan.Zero);
+ Contract.RequiresAlways(issued.Kind == DateTimeKind.Utc);
+ Contract.RequiresAlways(issued <= DateTime.UtcNow);
+
this.Handle = handle;
this.SecretKey = secret;
this.TotalLifeLength = totalLifeLength;
@@ -190,7 +194,9 @@ namespace DotNetOpenAuth.OpenId {
// re-instantiated on deserialization.
// For now, we just send out the secret key. We can derive the type from the length later.
byte[] secretKeyCopy = new byte[this.SecretKey.Length];
- this.SecretKey.CopyTo(secretKeyCopy, 0);
+ if (this.SecretKey.Length > 0) {
+ this.SecretKey.CopyTo(secretKeyCopy, 0);
+ }
return secretKeyCopy;
}
@@ -275,54 +281,17 @@ namespace DotNetOpenAuth.OpenId {
/// </summary>
/// <returns>The hash algorithm used for message signing.</returns>
protected abstract HashAlgorithm CreateHasher();
- }
-
- /// <summary>
- /// Code contract for the <see cref="Association"/> class.
- /// </summary>
- [ContractClassFor(typeof(Association))]
- internal abstract class AssociationContract : Association {
- /// <summary>
- /// Initializes a new instance of the <see cref="AssociationContract"/> class.
- /// </summary>
- private AssociationContract()
- : base(null, null, TimeSpan.Zero, DateTime.Now) {
- }
-
- /// <summary>
- /// Gets the length (in bits) of the hash this association creates when signing.
- /// </summary>
- public override int HashBitLength {
- get {
- Contract.Ensures(Contract.Result<int>() > 0);
- throw new NotImplementedException();
- }
- }
-
- /// <summary>
- /// The string to pass as the assoc_type value in the OpenID protocol.
- /// </summary>
- /// <param name="protocol">The protocol version of the message that the assoc_type value will be included in.</param>
- /// <returns>
- /// The value that should be used for the openid.assoc_type parameter.
- /// </returns>
- [Pure]
- internal override string GetAssociationType(Protocol protocol) {
- Contract.Requires(protocol != null);
- throw new NotImplementedException();
- }
+#if CONTRACTS_FULL
/// <summary>
- /// Returns the specific hash algorithm used for message signing.
+ /// Verifies conditions that should be true for any valid state of this object.
/// </summary>
- /// <returns>
- /// The hash algorithm used for message signing.
- /// </returns>
- [Pure]
- protected override HashAlgorithm CreateHasher() {
- Contract.Ensures(Contract.Result<HashAlgorithm>() != null);
- throw new NotImplementedException();
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ protected void ObjectInvariant() {
+ Contract.Invariant(!string.IsNullOrEmpty(this.Handle));
+ Contract.Invariant(this.TotalLifeLength > TimeSpan.Zero);
}
+#endif
}
-
}
diff --git a/src/DotNetOpenAuth/OpenId/AssociationContract.cs b/src/DotNetOpenAuth/OpenId/AssociationContract.cs
new file mode 100644
index 0000000..7a8398e
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/AssociationContract.cs
@@ -0,0 +1,65 @@
+//-----------------------------------------------------------------------
+// <copyright file="AssociationContract.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId {
+ using System;
+ using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.IO;
+ using System.Security.Cryptography;
+ using System.Text;
+ using DotNetOpenAuth.Configuration;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// Code contract for the <see cref="Association"/> class.
+ /// </summary>
+ [ContractClassFor(typeof(Association))]
+ internal abstract class AssociationContract : Association {
+ /// <summary>
+ /// Prevents a default instance of the <see cref="AssociationContract"/> class from being created.
+ /// </summary>
+ private AssociationContract()
+ : base(null, null, TimeSpan.Zero, DateTime.Now) {
+ }
+
+ /// <summary>
+ /// Gets the length (in bits) of the hash this association creates when signing.
+ /// </summary>
+ public override int HashBitLength {
+ get {
+ Contract.Ensures(Contract.Result<int>() > 0);
+ throw new NotImplementedException();
+ }
+ }
+
+ /// <summary>
+ /// The string to pass as the assoc_type value in the OpenID protocol.
+ /// </summary>
+ /// <param name="protocol">The protocol version of the message that the assoc_type value will be included in.</param>
+ /// <returns>
+ /// The value that should be used for the openid.assoc_type parameter.
+ /// </returns>
+ [Pure]
+ internal override string GetAssociationType(Protocol protocol) {
+ Contract.Requires(protocol != null);
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Returns the specific hash algorithm used for message signing.
+ /// </summary>
+ /// <returns>
+ /// The hash algorithm used for message signing.
+ /// </returns>
+ [Pure]
+ protected override HashAlgorithm CreateHasher() {
+ Contract.Ensures(Contract.Result<HashAlgorithm>() != null);
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Associations.cs b/src/DotNetOpenAuth/OpenId/Associations.cs
index 5386ac9..9f8c1d4 100644
--- a/src/DotNetOpenAuth/OpenId/Associations.cs
+++ b/src/DotNetOpenAuth/OpenId/Associations.cs
@@ -5,13 +5,13 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OpenId {
- using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Linq;
using DotNetOpenAuth.Messaging;
-using System.Diagnostics.Contracts;
/// <summary>
/// A dictionary of handle/Association pairs.
@@ -109,5 +109,16 @@ using System.Diagnostics.Contracts;
}
}
}
+
+#if CONTRACTS_FULL
+ /// <summary>
+ /// Verifies conditions that should be true for any valid state of this object.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ protected void ObjectInvariant() {
+ Contract.Invariant(this.associations != null);
+ }
+#endif
}
}
diff --git a/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs b/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs
index df1dede..cbba342 100644
--- a/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs
+++ b/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs
@@ -67,9 +67,9 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="totalLifeLength">The time duration the association will be good for.</param>
private HmacShaAssociation(HmacSha typeIdentity, string handle, byte[] secret, TimeSpan totalLifeLength)
: base(handle, secret, totalLifeLength, DateTime.UtcNow) {
- ErrorUtilities.VerifyArgumentNotNull(typeIdentity, "typeIdentity");
- ErrorUtilities.VerifyNonZeroLength(handle, "handle");
- ErrorUtilities.VerifyArgumentNotNull(secret, "secret");
+ Contract.Requires(typeIdentity != null);
+ Contract.Requires(!String.IsNullOrEmpty(handle));
+ Contract.Requires(secret != null);
ErrorUtilities.VerifyProtocol(secret.Length == typeIdentity.SecretLength, OpenIdStrings.AssociationSecretAndTypeLengthMismatch, secret.Length, typeIdentity.GetAssociationType(Protocol.Default));
this.typeIdentity = typeIdentity;
@@ -171,6 +171,9 @@ namespace DotNetOpenAuth.OpenId {
lifetime = DumbSecretLifetime;
}
+ Contract.Assert(protocol != null); // All the way up to the method call, the condition holds, yet we get a Requires failure next
+ Contract.Assert(secret != null);
+ Contract.Assert(!String.IsNullOrEmpty(associationType));
return Create(protocol, associationType, handle, secret, lifetime);
}
@@ -265,7 +268,9 @@ namespace DotNetOpenAuth.OpenId {
/// </returns>
[Pure]
protected override HashAlgorithm CreateHasher() {
- return this.typeIdentity.CreateHasher(SecretKey);
+ var result = this.typeIdentity.CreateHasher(SecretKey);
+ Contract.Assume(result != null);
+ return result;
}
/// <summary>
diff --git a/src/DotNetOpenAuth/OpenId/IAssociationStore.cs b/src/DotNetOpenAuth/OpenId/IAssociationStore.cs
index 4939cec..bda64cf 100644
--- a/src/DotNetOpenAuth/OpenId/IAssociationStore.cs
+++ b/src/DotNetOpenAuth/OpenId/IAssociationStore.cs
@@ -4,9 +4,10 @@
// </copyright>
//-----------------------------------------------------------------------
-using System.Diagnostics.Contracts;
-using System;
namespace DotNetOpenAuth.OpenId {
+ using System;
+ using System.Diagnostics.Contracts;
+
/// <summary>
/// An enumeration that can specify how a given <see cref="Association"/> is used.
/// </summary>
@@ -96,39 +97,96 @@ namespace DotNetOpenAuth.OpenId {
void ClearExpiredAssociations();
}
+ /// <summary>
+ /// Code Contract for the <see cref="IAssociationStore&lt;TKey&gt;"/> class.
+ /// </summary>
+ /// <typeparam name="TKey">The type of the key.</typeparam>
[ContractClassFor(typeof(IAssociationStore<>))]
internal class IAssociationStoreContract<TKey> : IAssociationStore<TKey> {
#region IAssociationStore<TKey> Members
+ /// <summary>
+ /// Saves an <see cref="Association"/> for later recall.
+ /// </summary>
+ /// <param name="distinguishingFactor">The Uri (for relying parties) or Smart/Dumb (for providers).</param>
+ /// <param name="association">The association to store.</param>
+ /// <remarks>
+ /// TODO: what should implementations do on association handle conflict?
+ /// </remarks>
void IAssociationStore<TKey>.StoreAssociation(TKey distinguishingFactor, Association association) {
Contract.Requires(distinguishingFactor != null);
Contract.Requires(association != null);
throw new NotImplementedException();
}
+ /// <summary>
+ /// Gets the best association (the one with the longest remaining life) for a given key.
+ /// </summary>
+ /// <param name="distinguishingFactor">The Uri (for relying parties) or Smart/Dumb (for Providers).</param>
+ /// <param name="securityRequirements">The security requirements that the returned association must meet.</param>
+ /// <returns>
+ /// The requested association, or null if no unexpired <see cref="Association"/>s exist for the given key.
+ /// </returns>
+ /// <remarks>
+ /// In the event that multiple associations exist for the given
+ /// <paramref name="distinguishingFactor"/>, it is important for the
+ /// implementation for this method to use the <paramref name="securityRequirements"/>
+ /// to pick the best (highest grade or longest living as the host's policy may dictate)
+ /// association that fits the security requirements.
+ /// Associations that are returned that do not meet the security requirements will be
+ /// ignored and a new association created.
+ /// </remarks>
Association IAssociationStore<TKey>.GetAssociation(TKey distinguishingFactor, SecuritySettings securityRequirements) {
Contract.Requires(distinguishingFactor != null);
Contract.Requires(securityRequirements != null);
throw new NotImplementedException();
}
+ /// <summary>
+ /// Gets the association for a given key and handle.
+ /// </summary>
+ /// <param name="distinguishingFactor">The Uri (for relying parties) or Smart/Dumb (for Providers).</param>
+ /// <param name="handle">The handle of the specific association that must be recalled.</param>
+ /// <returns>
+ /// The requested association, or null if no unexpired <see cref="Association"/>s exist for the given key and handle.
+ /// </returns>
Association IAssociationStore<TKey>.GetAssociation(TKey distinguishingFactor, string handle) {
Contract.Requires(distinguishingFactor != null);
Contract.Requires(!String.IsNullOrEmpty(handle));
throw new NotImplementedException();
}
+ /// <summary>
+ /// Removes a specified handle that may exist in the store.
+ /// </summary>
+ /// <param name="distinguishingFactor">The Uri (for relying parties) or Smart/Dumb (for Providers).</param>
+ /// <param name="handle">The handle of the specific association that must be deleted.</param>
+ /// <returns>
+ /// True if the association existed in this store previous to this call.
+ /// </returns>
+ /// <remarks>
+ /// No exception should be thrown if the association does not exist in the store
+ /// before this call.
+ /// </remarks>
bool IAssociationStore<TKey>.RemoveAssociation(TKey distinguishingFactor, string handle) {
Contract.Requires(distinguishingFactor != null);
Contract.Requires(!String.IsNullOrEmpty(handle));
throw new NotImplementedException();
}
+ /// <summary>
+ /// Clears all expired associations from the store.
+ /// </summary>
+ /// <remarks>
+ /// If another algorithm is in place to periodically clear out expired associations,
+ /// this method call may be ignored.
+ /// This should be done frequently enough to avoid a memory leak, but sparingly enough
+ /// to not be a performance drain.
+ /// </remarks>
void IAssociationStore<TKey>.ClearExpiredAssociations() {
throw new NotImplementedException();
}
#endregion
}
-
}
diff --git a/src/DotNetOpenAuth/OpenId/Protocol.cs b/src/DotNetOpenAuth/OpenId/Protocol.cs
index b9f2cca..f38d8a0 100644
--- a/src/DotNetOpenAuth/OpenId/Protocol.cs
+++ b/src/DotNetOpenAuth/OpenId/Protocol.cs
@@ -11,6 +11,7 @@ namespace DotNetOpenAuth.OpenId {
using DotNetOpenAuth.Messaging;
using System.Globalization;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
/// <summary>
/// An enumeration of the OpenID protocol versions supported by this library.
@@ -34,7 +35,7 @@ namespace DotNetOpenAuth.OpenId {
/// Tracks the several versions of OpenID this library supports and the unique
/// constants to each version used in the protocol.
/// </summary>
- internal class Protocol {
+ internal sealed class Protocol {
/// <summary>
/// The value of the openid.ns parameter in the OpenID 2.0 specification.
/// </summary>
@@ -251,7 +252,7 @@ namespace DotNetOpenAuth.OpenId {
/// </summary>
public QueryArguments Args = new QueryArguments();
- internal class QueryParameters {
+ internal sealed class QueryParameters {
/// <summary>
/// The value "openid."
/// </summary>
@@ -317,18 +318,29 @@ namespace DotNetOpenAuth.OpenId {
public string dh_server_public = "dh_server_public";
public string enc_mac_key = "enc_mac_key";
public string mac_key = "mac_key";
+
+#if CONTRACTS_FULL
+ /// <summary>
+ /// Verifies conditions that should be true for any valid state of this object.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ private void ObjectInvariant() {
+ Contract.Invariant(!string.IsNullOrEmpty(this.Prefix));
+ }
+#endif
}
- internal class QueryArguments {
+ internal sealed class QueryArguments {
public ErrorCodes ErrorCode = new ErrorCodes();
public SessionTypes SessionType = new SessionTypes();
public SignatureAlgorithms SignatureAlgorithm = new SignatureAlgorithms();
public Modes Mode = new Modes();
public IsValidValues IsValid = new IsValidValues();
- internal class ErrorCodes {
+ internal sealed class ErrorCodes {
public string UnsupportedType = "unsupported-type";
}
- internal class SessionTypes {
+ internal sealed class SessionTypes {
/// <summary>
/// A preference order list of all supported session types.
/// </summary>
@@ -350,7 +362,7 @@ namespace DotNetOpenAuth.OpenId {
}
}
}
- internal class SignatureAlgorithms {
+ internal sealed class SignatureAlgorithms {
/// <summary>
/// A preference order list of signature algorithms we support.
/// </summary>
@@ -370,7 +382,7 @@ namespace DotNetOpenAuth.OpenId {
}
}
}
- internal class Modes {
+ internal sealed class Modes {
public string cancel = "cancel";
public string error = "error";
public string id_res = "id_res";
@@ -380,7 +392,7 @@ namespace DotNetOpenAuth.OpenId {
public string associate = "associate";
public string setup_needed = "id_res"; // V2 overrides this
}
- internal class IsValidValues {
+ internal sealed class IsValidValues {
public string True = "true";
public string False = "false";
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
index c3780a9..44ad111 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
@@ -309,16 +309,12 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// the user agent to allow the redirect with assertion to happen.
/// </returns>
public OutgoingWebResponse PrepareUnsolicitedAssertion(Uri providerEndpoint, Realm relyingParty, Identifier claimedIdentifier, Identifier localIdentifier, params IExtensionMessage[] extensions) {
- Contract.Requires(providerEndpoint != null);
- Contract.Requires(providerEndpoint.IsAbsoluteUri);
- Contract.Requires(relyingParty != null);
- Contract.Requires(claimedIdentifier != null);
- Contract.Requires(localIdentifier != null);
- ErrorUtilities.VerifyArgumentNotNull(providerEndpoint, "providerEndpoint");
- ErrorUtilities.VerifyArgumentNotNull(relyingParty, "relyingParty");
- ErrorUtilities.VerifyArgumentNotNull(claimedIdentifier, "claimedIdentifier");
- ErrorUtilities.VerifyArgumentNotNull(localIdentifier, "localIdentifier");
- ErrorUtilities.VerifyArgumentNamed(providerEndpoint.IsAbsoluteUri, "providerEndpoint", OpenIdStrings.AbsoluteUriRequired);
+ Contract.RequiresAlways(providerEndpoint != null);
+ Contract.RequiresAlways(providerEndpoint.IsAbsoluteUri);
+ Contract.RequiresAlways(relyingParty != null);
+ Contract.RequiresAlways(claimedIdentifier != null);
+ Contract.RequiresAlways(localIdentifier != null);
+ Contract.RequiresAlways(this.WebRequestHandler != null);
// Although the RP should do their due diligence to make sure that this OP
// is authorized to send an assertion for the given claimed identifier,
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
index 2e22aec..9c24635 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
@@ -237,11 +237,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
Contract.Requires(userSuppliedIdentifier != null);
Contract.Requires(relyingParty != null);
Contract.Requires(realm != null);
-
- // We have a long data validation and preparation process
- ErrorUtilities.VerifyArgumentNotNull(userSuppliedIdentifier, "userSuppliedIdentifier");
- ErrorUtilities.VerifyArgumentNotNull(relyingParty, "relyingParty");
- ErrorUtilities.VerifyArgumentNotNull(realm, "realm");
+ Contract.Ensures(Contract.Result<IEnumerable<AuthenticationRequest>>() != null);
// Normalize the portion of the return_to path that correlates to the realm for capitalization.
// (so that if a web app base path is /MyApp/, but the URL of this request happens to be
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
index 3a4a92a..b665a4b 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
@@ -18,6 +18,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
using DotNetOpenAuth.OpenId.ChannelElements;
using DotNetOpenAuth.OpenId.Extensions;
using DotNetOpenAuth.OpenId.Messages;
+ using System.Diagnostics.CodeAnalysis;
/// <summary>
/// A delegate that decides whether a given OpenID Provider endpoint may be
@@ -162,7 +163,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
internal set {
Contract.Requires(value != null);
- ErrorUtilities.VerifyArgumentNotNull(value, "value");
this.securitySettings = value;
this.AssociationManager.SecuritySettings = value;
}
@@ -195,8 +195,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
set {
- Contract.Requires(value != null);
- ErrorUtilities.VerifyArgumentNotNull(value, "value");
+ Contract.RequiresAlways(value != null);
this.endpointOrder = value;
}
}
@@ -464,6 +463,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
Contract.Requires(userSuppliedIdentifier != null);
Contract.Requires(realm != null);
Contract.Ensures(Contract.Result<IEnumerable<IAuthenticationRequest>>() != null);
+ Contract.Ensures(Contract.ForAll(Contract.Result<IEnumerable<IAuthenticationRequest>>(), el => el != null));
+
ErrorUtilities.VerifyHttpContext();
// Build the return_to URL
@@ -526,6 +527,19 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
return this.CreateRequests(userSuppliedIdentifier, new Realm(realmUrl.Uri));
}
+#if CONTRACTS_FULL
+ /// <summary>
+ /// Verifies conditions that should be true for any valid state of this object.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ private void ObjectInvariant() {
+ Contract.Invariant(this.securitySettings != null);
+ Contract.Invariant(this.channel != null);
+ Contract.Invariant(this.endpointOrder != null);
+ }
+#endif
+
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
diff --git a/src/DotNetOpenAuth/OpenId/XriIdentifier.cs b/src/DotNetOpenAuth/OpenId/XriIdentifier.cs
index 1bb03ee..6d491a2 100644
--- a/src/DotNetOpenAuth/OpenId/XriIdentifier.cs
+++ b/src/DotNetOpenAuth/OpenId/XriIdentifier.cs
@@ -156,7 +156,6 @@ namespace DotNetOpenAuth.OpenId {
/// </returns>
internal static bool IsValidXri(string xri) {
Contract.Requires((xri != null && xri.Length > 0) || !string.IsNullOrEmpty(xri));
- ErrorUtilities.VerifyNonZeroLength(xri, "xri");
xri = xri.Trim();
// TODO: better validation code here