summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2009-03-30 22:21:18 -0700
committerAndrew Arnott <andrewarnott@gmail.com>2009-04-22 10:09:49 -0700
commitdb31c422c600edddeee8ac4d7db0fc80c0e143bd (patch)
tree7ef0cd960570c4acef25aec25b426dfe01ee3889
parent05002eda38c0bf1b82b8bb44d893d9f2cee53d53 (diff)
downloadDotNetOpenAuth-db31c422c600edddeee8ac4d7db0fc80c0e143bd.zip
DotNetOpenAuth-db31c422c600edddeee8ac4d7db0fc80c0e143bd.tar.gz
DotNetOpenAuth-db31c422c600edddeee8ac4d7db0fc80c0e143bd.tar.bz2
More work on contracts.
-rw-r--r--src/DotNetOpenAuth.sln2
-rw-r--r--src/DotNetOpenAuth/Configuration/OpenIdElement.cs11
-rw-r--r--src/DotNetOpenAuth/InfoCard/ClaimType.cs2
-rw-r--r--src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs6
-rw-r--r--src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs46
-rw-r--r--src/DotNetOpenAuth/Messaging/NetworkDirectWebResponse.cs2
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs1
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs3
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs26
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs11
-rw-r--r--src/DotNetOpenAuth/OpenId/Association.cs85
-rw-r--r--src/DotNetOpenAuth/OpenId/Associations.cs10
-rw-r--r--src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs33
-rw-r--r--src/DotNetOpenAuth/OpenId/IAssociationStore.cs39
14 files changed, 213 insertions, 64 deletions
diff --git a/src/DotNetOpenAuth.sln b/src/DotNetOpenAuth.sln
index 87c3d3f..71aa203 100644
--- a/src/DotNetOpenAuth.sln
+++ b/src/DotNetOpenAuth.sln
@@ -7,7 +7,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.Test", "DotN
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{20B5E173-C3C4-49F8-BD25-E69044075B4D}"
ProjectSection(SolutionItems) = preProject
- DotNetOpenAuth.vsmdi = DotNetOpenAuth.vsmdi
+ dotnetopenauth.vsmdi = dotnetopenauth.vsmdi
LocalTestRun.testrunconfig = LocalTestRun.testrunconfig
..\doc\README.Bin.html = ..\doc\README.Bin.html
..\doc\README.html = ..\doc\README.html
diff --git a/src/DotNetOpenAuth/Configuration/OpenIdElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdElement.cs
index 25e067e..dd48175 100644
--- a/src/DotNetOpenAuth/Configuration/OpenIdElement.cs
+++ b/src/DotNetOpenAuth/Configuration/OpenIdElement.cs
@@ -53,8 +53,15 @@ namespace DotNetOpenAuth.Configuration {
[ConfigurationProperty(MaxAuthenticationTimePropertyName, DefaultValue = "0:05")] // 5 minutes
[PositiveTimeSpanValidator]
internal TimeSpan MaxAuthenticationTime {
- get { return (TimeSpan)this[MaxAuthenticationTimePropertyName]; }
- set { this[MaxAuthenticationTimePropertyName] = value; }
+ get {
+ Contract.Ensures(Contract.Result<TimeSpan>() > TimeSpan.Zero);
+ return (TimeSpan)this[MaxAuthenticationTimePropertyName];
+ }
+
+ set {
+ Contract.Requires(value > TimeSpan.Zero);
+ this[MaxAuthenticationTimePropertyName] = value;
+ }
}
/// <summary>
diff --git a/src/DotNetOpenAuth/InfoCard/ClaimType.cs b/src/DotNetOpenAuth/InfoCard/ClaimType.cs
index 5a25ef8..96a3d32 100644
--- a/src/DotNetOpenAuth/InfoCard/ClaimType.cs
+++ b/src/DotNetOpenAuth/InfoCard/ClaimType.cs
@@ -9,12 +9,14 @@ namespace DotNetOpenAuth.InfoCard {
using System.ComponentModel;
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.
/// </summary>
[PersistChildren(false)]
[Serializable]
+ [ContractVerification(true)]
public class ClaimType {
/// <summary>
/// Initializes a new instance of the <see cref="ClaimType"/> class.
diff --git a/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs b/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs
index 65cbba1..0a24cea 100644
--- a/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs
+++ b/src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs
@@ -15,6 +15,7 @@ namespace DotNetOpenAuth.Messaging {
/// <summary>
/// Cached details on the response from a direct web request to a remote party.
/// </summary>
+ [ContractVerification(true)]
[DebuggerDisplay("{Status} {ContentType.MediaType}, length: {ResponseStream.Length}")]
internal class CachedDirectWebResponse : IncomingWebResponse {
/// <summary>
@@ -54,8 +55,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) {
- ErrorUtilities.VerifyArgumentNotNull(responseStream, "responseStream");
-
+ Contract.Requires(responseStream != null);
this.responseStream = responseStream;
}
@@ -149,7 +149,7 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="maximumBytesToRead">The maximum bytes to cache.</param>
/// <returns>The seekable Stream instance that contains a copy of what was returned in the HTTP response.</returns>
private static MemoryStream CacheNetworkStreamAndClose(HttpWebResponse response, int maximumBytesToRead) {
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.Requires(response != null);
// Now read and cache the network stream
Stream networkStream = response.GetResponseStream();
diff --git a/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs b/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs
index dee81dc..bc30abc 100644
--- a/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs
+++ b/src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.Messaging {
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.Net;
@@ -17,6 +18,8 @@ namespace DotNetOpenAuth.Messaging {
/// <summary>
/// Details on the incoming response from a direct web request to a remote party.
/// </summary>
+ [ContractVerification(true)]
+ [ContractClass(typeof(IncomingWebResponseContract))]
public abstract class IncomingWebResponse : IDisposable {
/// <summary>
/// The encoding to use in reading a response that does not declare its own content encoding.
@@ -37,8 +40,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="requestUri">The original request URI.</param>
/// <param name="response">The response to initialize from. The network stream is used by this class directly.</param>
protected IncomingWebResponse(Uri requestUri, HttpWebResponse response) {
- ErrorUtilities.VerifyArgumentNotNull(requestUri, "requestUri");
- ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ Contract.RequiresAlways(requestUri != null);
+ Contract.RequiresAlways(response != null);
this.RequestUri = requestUri;
if (!string.IsNullOrEmpty(response.ContentType)) {
@@ -64,7 +67,8 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="contentType">Type of the content.</param>
/// <param name="contentEncoding">The content encoding.</param>
protected IncomingWebResponse(Uri requestUri, Uri responseUri, WebHeaderCollection headers, HttpStatusCode statusCode, string contentType, string contentEncoding) {
- ErrorUtilities.VerifyArgumentNotNull(requestUri, "requestUri");
+ Contract.RequiresAlways(requestUri != null);
+
this.RequestUri = requestUri;
this.Status = statusCode;
if (!string.IsNullOrEmpty(contentType)) {
@@ -185,4 +189,40 @@ namespace DotNetOpenAuth.Messaging {
}
}
}
+
+ [ContractClassFor(typeof(IncomingWebResponse))]
+ internal 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/NetworkDirectWebResponse.cs b/src/DotNetOpenAuth/Messaging/NetworkDirectWebResponse.cs
index 2ee7feb..3bbec6e 100644
--- a/src/DotNetOpenAuth/Messaging/NetworkDirectWebResponse.cs
+++ b/src/DotNetOpenAuth/Messaging/NetworkDirectWebResponse.cs
@@ -10,11 +10,13 @@ namespace DotNetOpenAuth.Messaging {
using System.IO;
using System.Net;
using System.Text;
+using System.Diagnostics.Contracts;
/// <summary>
/// A live network HTTP response
/// </summary>
[DebuggerDisplay("{Status} {ContentType.MediaType}")]
+ [ContractVerification(true)]
internal class NetworkDirectWebResponse : IncomingWebResponse, IDisposable {
/// <summary>
/// The network response object, used to initialize this instance, that still needs
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs
index 7ac21f2..9baa67d 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs
@@ -64,7 +64,6 @@ namespace DotNetOpenAuth.Messaging.Reflection {
internal MessageDescription Get(IMessage message) {
Contract.Requires(message != null);
Contract.Ensures(Contract.Result<MessageDescription>() != null);
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
return this.Get(message.GetType(), message.Version);
}
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
index b721b63..ec38867 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs
@@ -16,6 +16,7 @@ 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)]
internal class MessageDictionary : IDictionary<string, string> {
/// <summary>
/// The <see cref="IMessage"/> instance manipulated by this dictionary.
@@ -35,8 +36,6 @@ namespace DotNetOpenAuth.Messaging.Reflection {
internal MessageDictionary(IMessage message, MessageDescription description) {
Contract.Requires(message != null);
Contract.Requires(description != null);
- ErrorUtilities.VerifyArgumentNotNull(message, "message");
- ErrorUtilities.VerifyArgumentNotNull(description, "description");
this.message = message;
this.description = description;
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
index 0e26860..f922cc8 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
using System.Globalization;
using System.Net.Security;
using System.Reflection;
@@ -17,6 +18,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <summary>
/// Describes an individual member of a message and assists in its serialization.
/// </summary>
+ [ContractVerification(true)]
internal class MessagePart {
/// <summary>
/// A map of converters that help serialize custom objects to string values and back again.
@@ -79,26 +81,12 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// serialization requirements of the message part.
/// </param>
internal MessagePart(MemberInfo member, MessagePartAttribute attribute) {
- if (member == null) {
- throw new ArgumentNullException("member");
- }
+ Contract.Requires(member != null);
+ Contract.Requires(member is FieldInfo || member is PropertyInfo);
+ Contract.Requires(attribute != null);
this.field = member as FieldInfo;
this.property = member as PropertyInfo;
- if (this.field == null && this.property == null) {
- throw new ArgumentException(
- string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.UnexpectedType,
- typeof(FieldInfo).Name + ", " + typeof(PropertyInfo).Name,
- member.GetType().Name),
- "member");
- }
-
- if (attribute == null) {
- throw new ArgumentNullException("attribute");
- }
-
this.Name = attribute.Name ?? member.Name;
this.RequiredProtection = attribute.RequiredProtection;
this.IsRequired = attribute.IsRequired;
@@ -166,9 +154,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <param name="message">The message instance containing the member whose value should be set.</param>
/// <param name="value">The string representation of the value to set.</param>
internal void SetValue(IMessage message, string value) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
+ Contract.Requires(message != null);
try {
if (this.IsConstantValue) {
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs b/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs
index bdc2d7f..b31e4d8 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs
@@ -6,10 +6,12 @@
namespace DotNetOpenAuth.Messaging.Reflection {
using System;
+ using System.Diagnostics.Contracts;
/// <summary>
/// A pair of conversion functions to map some type to a string and back again.
/// </summary>
+ [ContractVerification(true)]
internal struct ValueMapping {
/// <summary>
/// The mapping function that converts some custom type to a string.
@@ -27,13 +29,8 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <param name="toString">The mapping function that converts some custom type to a string.</param>
/// <param name="toValue">The mapping function that converts a string to some custom type.</param>
internal ValueMapping(Func<object, string> toString, Func<string, object> toValue) {
- if (toString == null) {
- throw new ArgumentNullException("toString");
- }
-
- if (toValue == null) {
- throw new ArgumentNullException("toValue");
- }
+ Contract.Requires(toString != null);
+ Contract.Requires(toValue != null);
this.ValueToString = toString;
this.StringToValue = toValue;
diff --git a/src/DotNetOpenAuth/OpenId/Association.cs b/src/DotNetOpenAuth/OpenId/Association.cs
index eb7c880..7514cf9 100644
--- a/src/DotNetOpenAuth/OpenId/Association.cs
+++ b/src/DotNetOpenAuth/OpenId/Association.cs
@@ -8,6 +8,7 @@ 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;
@@ -23,6 +24,8 @@ namespace DotNetOpenAuth.OpenId {
/// (dumb associations).
/// </remarks>
[DebuggerDisplay("Handle = {Handle}, Expires = {Expires}")]
+ [ContractVerification(true)]
+ [ContractClass(typeof(AssociationContract))]
public abstract class Association {
/// <summary>
/// Initializes a new instance of the <see cref="Association"/> class.
@@ -32,9 +35,8 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="totalLifeLength">How long the association will be useful.</param>
/// <param name="issued">When this association was originally issued by the Provider.</param>
protected Association(string handle, byte[] secret, TimeSpan totalLifeLength, DateTime issued) {
- ErrorUtilities.VerifyNonZeroLength(handle, "handle");
- ErrorUtilities.VerifyArgumentNotNull(secret, "secret");
-
+ Contract.RequiresAlways(!string.IsNullOrEmpty(handle));
+ Contract.RequiresAlways(secret != null);
this.Handle = handle;
this.SecretKey = secret;
this.TotalLifeLength = totalLifeLength;
@@ -85,7 +87,10 @@ namespace DotNetOpenAuth.OpenId {
/// Never negative (counter runs to zero).
/// </summary>
protected internal long SecondsTillExpiration {
- get { return Math.Max(0, (long)this.TimeTillExpiration.TotalSeconds); }
+ get {
+ Contract.Ensures(Contract.Result<long>() >= 0);
+ return Math.Max(0, (long)this.TimeTillExpiration.TotalSeconds);
+ }
}
/// <summary>
@@ -98,7 +103,10 @@ namespace DotNetOpenAuth.OpenId {
/// Gets the duration a secret key used for signing dumb client requests will be good for.
/// </summary>
protected static TimeSpan DumbSecretLifetime {
- get { return DotNetOpenAuthSection.Configuration.OpenId.MaxAuthenticationTime; }
+ get {
+ Contract.Ensures(Contract.Result<TimeSpan>() > TimeSpan.Zero);
+ return DotNetOpenAuthSection.Configuration.OpenId.MaxAuthenticationTime;
+ }
}
/// <summary>
@@ -113,7 +121,10 @@ namespace DotNetOpenAuth.OpenId {
/// Associations that are not likely to last the duration of a user login are not worth using at all.
/// </remarks>
private static TimeSpan MinimumUsefulAssociationLifetime {
- get { return DotNetOpenAuthSection.Configuration.OpenId.MaxAuthenticationTime; }
+ get {
+ Contract.Ensures(Contract.Result<TimeSpan>() > TimeSpan.Zero);
+ return DotNetOpenAuthSection.Configuration.OpenId.MaxAuthenticationTime;
+ }
}
/// <summary>
@@ -143,12 +154,10 @@ namespace DotNetOpenAuth.OpenId {
/// <see cref="IAssociationStore&lt;TKey&gt;.GetAssociation(TKey, SecuritySettings)"/> method.
/// </returns>
public static Association Deserialize(string handle, DateTime expires, byte[] privateData) {
- if (string.IsNullOrEmpty(handle)) {
- throw new ArgumentNullException("handle");
- }
- if (privateData == null) {
- throw new ArgumentNullException("privateData");
- }
+ Contract.RequiresAlways(!String.IsNullOrEmpty(handle));
+ Contract.RequiresAlways(privateData != null);
+ Contract.Ensures(Contract.Result<Association>() != null);
+
expires = expires.ToUniversalTime();
TimeSpan remainingLifeLength = expires - DateTime.UtcNow;
byte[] secret = privateData; // the whole of privateData is the secret key for now.
@@ -174,6 +183,8 @@ namespace DotNetOpenAuth.OpenId {
/// in this byte array, as they are useful for fast database lookup and are persisted separately.
/// </remarks>
public byte[] SerializePrivateData() {
+ Contract.Ensures(Contract.Result<byte[]>() != null);
+
// We may want to encrypt this secret using the machine.config private key,
// and add data regarding which Association derivative will need to be
// re-instantiated on deserialization.
@@ -253,6 +264,7 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="data">The data to sign. This data will not be changed (the signature is the return value).</param>
/// <returns>The calculated signature of the data.</returns>
protected internal byte[] Sign(byte[] data) {
+ Contract.Requires(data != null);
using (HashAlgorithm hasher = this.CreateHasher()) {
return hasher.ComputeHash(data);
}
@@ -264,4 +276,53 @@ namespace DotNetOpenAuth.OpenId {
/// <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();
+ }
+
+ /// <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 fe2be42..5386ac9 100644
--- a/src/DotNetOpenAuth/OpenId/Associations.cs
+++ b/src/DotNetOpenAuth/OpenId/Associations.cs
@@ -11,6 +11,7 @@ namespace DotNetOpenAuth.OpenId {
using System.Diagnostics;
using System.Linq;
using DotNetOpenAuth.Messaging;
+using System.Diagnostics.Contracts;
/// <summary>
/// A dictionary of handle/Association pairs.
@@ -21,6 +22,7 @@ namespace DotNetOpenAuth.OpenId {
/// can break if the collection is changed by another thread during enumeration.
/// </remarks>
[DebuggerDisplay("Count = {assocs.Count}")]
+ [ContractVerification(true)]
internal class Associations {
/// <summary>
/// The lookup table where keys are the association handles and values are the associations themselves.
@@ -44,6 +46,8 @@ namespace DotNetOpenAuth.OpenId {
/// </remarks>
public IEnumerable<Association> Best {
get {
+ Contract.Ensures(Contract.Result<IEnumerable<Association>>() != null);
+
lock (this.associations) {
return this.associations.OrderByDescending(assoc => assoc.Issued);
}
@@ -55,6 +59,8 @@ namespace DotNetOpenAuth.OpenId {
/// </summary>
/// <param name="association">The association to add to the collection.</param>
public void Set(Association association) {
+ Contract.Requires(association != null);
+ Contract.Ensures(this.Get(association.Handle) == association);
ErrorUtilities.VerifyArgumentNotNull(association, "association");
lock (this.associations) {
this.associations.Remove(association.Handle); // just in case one already exists.
@@ -67,7 +73,10 @@ namespace DotNetOpenAuth.OpenId {
/// </summary>
/// <param name="handle">The handle to the required association.</param>
/// <returns>The desired association, or null if none with the given handle could be found.</returns>
+ [Pure]
public Association Get(string handle) {
+ Contract.Requires(!string.IsNullOrEmpty(handle));
+
lock (this.associations) {
if (this.associations.Contains(handle)) {
return this.associations[handle];
@@ -83,6 +92,7 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="handle">The handle to the required association.</param>
/// <returns>Whether an <see cref="Association"/> with the given handle was in the collection for removal.</returns>
public bool Remove(string handle) {
+ Contract.Requires(!string.IsNullOrEmpty(handle));
lock (this.associations) {
return this.associations.Remove(handle);
}
diff --git a/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs b/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs
index d35dcd1..df1dede 100644
--- a/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs
+++ b/src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OpenId {
using System;
using System.Collections.Generic;
using System.Diagnostics;
+ using System.Diagnostics.Contracts;
using System.Linq;
using System.Security.Cryptography;
using DotNetOpenAuth.Messaging;
@@ -18,6 +19,7 @@ namespace DotNetOpenAuth.OpenId {
/// <summary>
/// An association that uses the HMAC-SHA family of algorithms for message signing.
/// </summary>
+ [ContractVerification(true)]
internal class HmacShaAssociation : Association {
/// <summary>
/// The default lifetime of a shared association when no lifetime is given
@@ -93,9 +95,10 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="totalLifeLength">How long the association will be good for.</param>
/// <returns>The newly created association.</returns>
public static HmacShaAssociation Create(Protocol protocol, string associationType, string handle, byte[] secret, TimeSpan totalLifeLength) {
- ErrorUtilities.VerifyArgumentNotNull(protocol, "protocol");
- ErrorUtilities.VerifyNonZeroLength(associationType, "associationType");
- ErrorUtilities.VerifyArgumentNotNull(secret, "secret");
+ Contract.Requires(protocol != null);
+ Contract.Requires(!String.IsNullOrEmpty(associationType));
+ Contract.Requires(secret != null);
+ Contract.Ensures(Contract.Result<HmacShaAssociation>() != null);
HmacSha match = hmacShaAssociationTypes.FirstOrDefault(sha => String.Equals(sha.GetAssociationType(protocol), associationType, StringComparison.Ordinal));
ErrorUtilities.VerifyProtocol(match != null, OpenIdStrings.NoAssociationTypeFoundByName, associationType);
@@ -110,8 +113,9 @@ namespace DotNetOpenAuth.OpenId {
/// <param name="totalLifeLength">Total lifetime.</param>
/// <returns>The newly created association.</returns>
public static HmacShaAssociation Create(string handle, byte[] secret, TimeSpan totalLifeLength) {
- ErrorUtilities.VerifyNonZeroLength(handle, "handle");
- ErrorUtilities.VerifyArgumentNotNull(secret, "secret");
+ Contract.Requires(!String.IsNullOrEmpty(handle));
+ Contract.Requires(secret != null);
+ Contract.Ensures(Contract.Result<HmacShaAssociation>() != null);
HmacSha shaType = hmacShaAssociationTypes.FirstOrDefault(sha => sha.SecretLength == secret.Length);
ErrorUtilities.VerifyProtocol(shaType != null, OpenIdStrings.NoAssociationTypeFoundByLength, secret.Length);
@@ -144,9 +148,10 @@ namespace DotNetOpenAuth.OpenId {
/// The new association is NOT automatically put into an association store. This must be done by the caller.
/// </remarks>
internal static HmacShaAssociation Create(Protocol protocol, string associationType, AssociationRelyingPartyType associationUse, ProviderSecuritySettings securitySettings) {
- ErrorUtilities.VerifyArgumentNotNull(protocol, "protocol");
- ErrorUtilities.VerifyNonZeroLength(associationType, "associationType");
- ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
+ Contract.Requires(protocol != null);
+ Contract.Requires(!String.IsNullOrEmpty(associationType));
+ Contract.Requires(securitySettings != null);
+ Contract.Ensures(Contract.Result<HmacShaAssociation>() != null);
// Generate the handle. It must be unique, and preferably unpredictable,
// so we use a time element and a random data element to generate it.
@@ -184,8 +189,8 @@ namespace DotNetOpenAuth.OpenId {
/// True if a qualifying association could be found; false otherwise.
/// </returns>
internal static bool TryFindBestAssociation(Protocol protocol, bool highSecurityIsBetter, SecuritySettings securityRequirements, bool requireMatchingDHSessionType, out string associationType, out string sessionType) {
- ErrorUtilities.VerifyArgumentNotNull(protocol, "protocol");
- ErrorUtilities.VerifyArgumentNotNull(securityRequirements, "securityRequirements");
+ Contract.Requires(protocol != null);
+ Contract.Requires(securityRequirements != null);
associationType = null;
sessionType = null;
@@ -225,9 +230,9 @@ namespace DotNetOpenAuth.OpenId {
/// <c>true</c> if the named association and session types are compatible; otherwise, <c>false</c>.
/// </returns>
internal static bool IsDHSessionCompatible(Protocol protocol, string associationType, string sessionType) {
- ErrorUtilities.VerifyArgumentNotNull(protocol, "protocol");
- ErrorUtilities.VerifyNonZeroLength(associationType, "associationType");
- ErrorUtilities.VerifyArgumentNotNull(sessionType, "sessionType");
+ Contract.Requires(protocol != null);
+ Contract.Requires(!String.IsNullOrEmpty(associationType));
+ Contract.Requires(sessionType != null);
// All association types can work when no DH session is used at all.
if (string.Equals(sessionType, protocol.Args.SessionType.NoEncryption, StringComparison.Ordinal)) {
@@ -247,6 +252,7 @@ namespace DotNetOpenAuth.OpenId {
/// <returns>
/// The value that should be used for the openid.assoc_type parameter.
/// </returns>
+ [Pure]
internal override string GetAssociationType(Protocol protocol) {
return this.typeIdentity.GetAssociationType(protocol);
}
@@ -257,6 +263,7 @@ namespace DotNetOpenAuth.OpenId {
/// <returns>
/// The hash algorithm used for message signing.
/// </returns>
+ [Pure]
protected override HashAlgorithm CreateHasher() {
return this.typeIdentity.CreateHasher(SecretKey);
}
diff --git a/src/DotNetOpenAuth/OpenId/IAssociationStore.cs b/src/DotNetOpenAuth/OpenId/IAssociationStore.cs
index 2376b0d..4939cec 100644
--- a/src/DotNetOpenAuth/OpenId/IAssociationStore.cs
+++ b/src/DotNetOpenAuth/OpenId/IAssociationStore.cs
@@ -4,6 +4,8 @@
// </copyright>
//-----------------------------------------------------------------------
+using System.Diagnostics.Contracts;
+using System;
namespace DotNetOpenAuth.OpenId {
/// <summary>
/// An enumeration that can specify how a given <see cref="Association"/> is used.
@@ -33,6 +35,7 @@ namespace DotNetOpenAuth.OpenId {
/// <see cref="System.Uri"/> for consumers (to distinguish associations across servers) or
/// <see cref="AssociationRelyingPartyType"/> for providers (to distinguish dumb and smart client associations).
/// </typeparam>
+ [ContractClass(typeof(IAssociationStoreContract<>))]
public interface IAssociationStore<TKey> {
/// <summary>
/// Saves an <see cref="Association"/> for later recall.
@@ -92,4 +95,40 @@ namespace DotNetOpenAuth.OpenId {
/// </remarks>
void ClearExpiredAssociations();
}
+
+ [ContractClassFor(typeof(IAssociationStore<>))]
+ internal class IAssociationStoreContract<TKey> : IAssociationStore<TKey> {
+ #region IAssociationStore<TKey> Members
+
+ void IAssociationStore<TKey>.StoreAssociation(TKey distinguishingFactor, Association association) {
+ Contract.Requires(distinguishingFactor != null);
+ Contract.Requires(association != null);
+ throw new NotImplementedException();
+ }
+
+ Association IAssociationStore<TKey>.GetAssociation(TKey distinguishingFactor, SecuritySettings securityRequirements) {
+ Contract.Requires(distinguishingFactor != null);
+ Contract.Requires(securityRequirements != null);
+ throw new NotImplementedException();
+ }
+
+ Association IAssociationStore<TKey>.GetAssociation(TKey distinguishingFactor, string handle) {
+ Contract.Requires(distinguishingFactor != null);
+ Contract.Requires(!String.IsNullOrEmpty(handle));
+ throw new NotImplementedException();
+ }
+
+ bool IAssociationStore<TKey>.RemoveAssociation(TKey distinguishingFactor, string handle) {
+ Contract.Requires(distinguishingFactor != null);
+ Contract.Requires(!String.IsNullOrEmpty(handle));
+ throw new NotImplementedException();
+ }
+
+ void IAssociationStore<TKey>.ClearExpiredAssociations() {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+
}