diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2009-03-30 22:21:18 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2009-04-22 10:09:49 -0700 |
commit | db31c422c600edddeee8ac4d7db0fc80c0e143bd (patch) | |
tree | 7ef0cd960570c4acef25aec25b426dfe01ee3889 | |
parent | 05002eda38c0bf1b82b8bb44d893d9f2cee53d53 (diff) | |
download | DotNetOpenAuth-db31c422c600edddeee8ac4d7db0fc80c0e143bd.zip DotNetOpenAuth-db31c422c600edddeee8ac4d7db0fc80c0e143bd.tar.gz DotNetOpenAuth-db31c422c600edddeee8ac4d7db0fc80c0e143bd.tar.bz2 |
More work on contracts.
-rw-r--r-- | src/DotNetOpenAuth.sln | 2 | ||||
-rw-r--r-- | src/DotNetOpenAuth/Configuration/OpenIdElement.cs | 11 | ||||
-rw-r--r-- | src/DotNetOpenAuth/InfoCard/ClaimType.cs | 2 | ||||
-rw-r--r-- | src/DotNetOpenAuth/Messaging/CachedDirectWebResponse.cs | 6 | ||||
-rw-r--r-- | src/DotNetOpenAuth/Messaging/IncomingWebResponse.cs | 46 | ||||
-rw-r--r-- | src/DotNetOpenAuth/Messaging/NetworkDirectWebResponse.cs | 2 | ||||
-rw-r--r-- | src/DotNetOpenAuth/Messaging/Reflection/MessageDescriptionCollection.cs | 1 | ||||
-rw-r--r-- | src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs | 3 | ||||
-rw-r--r-- | src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs | 26 | ||||
-rw-r--r-- | src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs | 11 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OpenId/Association.cs | 85 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OpenId/Associations.cs | 10 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OpenId/HmacShaAssociation.cs | 33 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OpenId/IAssociationStore.cs | 39 |
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<TKey>.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 + } + } |