summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2008-04-03 22:27:37 -0700
committerAndrew Arnott <andrewarnott@gmail.com>2008-04-03 22:27:37 -0700
commit22c48ff6b837476ee9e161c5a176ec7247046a9d (patch)
tree94fbb54c940778eb1d1fc0664fa7834ec8292977
parentf46074e109ca11fda72946a699af4a899c0b1812 (diff)
downloadDotNetOpenAuth-22c48ff6b837476ee9e161c5a176ec7247046a9d.zip
DotNetOpenAuth-22c48ff6b837476ee9e161c5a176ec7247046a9d.tar.gz
DotNetOpenAuth-22c48ff6b837476ee9e161c5a176ec7247046a9d.tar.bz2
Exposed the members necessary for Associations to be persisted in a remote store.
-rw-r--r--src/DotNetOpenId/Association.cs58
-rw-r--r--src/DotNetOpenId/Associations.cs4
-rw-r--r--src/DotNetOpenId/IAssociationStore.cs16
-rw-r--r--src/DotNetOpenId/RelyingParty/INonceStore.cs26
-rw-r--r--src/DotNetOpenId/Strings.Designer.cs9
-rw-r--r--src/DotNetOpenId/Strings.resx3
6 files changed, 113 insertions, 3 deletions
diff --git a/src/DotNetOpenId/Association.cs b/src/DotNetOpenId/Association.cs
index 869df16..afa9371 100644
--- a/src/DotNetOpenId/Association.cs
+++ b/src/DotNetOpenId/Association.cs
@@ -10,11 +10,44 @@ using System.Diagnostics.CodeAnalysis;
namespace DotNetOpenId {
public abstract class Association {
protected Association(string handle, byte[] secret, TimeSpan totalLifeLength, DateTime issued) {
+ if (string.IsNullOrEmpty(handle)) throw new ArgumentNullException("handle");
+ if (secret == null) throw new ArgumentNullException("secret");
Handle = handle;
SecretKey = secret;
TotalLifeLength = totalLifeLength;
Issued = cutToSecond(issued);
}
+ /// <summary>
+ /// Re-instantiates an <see cref="Association"/> previously persisted in a database or some
+ /// other shared store.
+ /// </summary>
+ /// <param name="handle">
+ /// The <see cref="Handle"/> property of the previous <see cref="Association"/> instance.
+ /// </param>
+ /// <param name="expires">
+ /// The value of the <see cref="Expires"/> property of the previous <see cref="Association"/> instance.
+ /// </param>
+ /// <param name="privateData">
+ /// The byte array returned by a call to <see cref="SerializePrivateData"/> on the previous
+ /// <see cref="Association"/> instance.
+ /// </param>
+ /// <returns>
+ /// The newly dehydrated <see cref="Association"/>, which can be returned
+ /// from a custom association store's <see cref="IAssociationStore.GetAssociation"/> 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");
+ expires = expires.ToUniversalTime();
+ TimeSpan remainingLifeLength = expires - DateTime.UtcNow;
+ byte[] secret = privateData; // the whole of privateData is the secret key for now.
+ // We figure out what derived type to instantiate based on the length of the secret.
+ if(secret.Length == CryptUtil.Sha1.HashSize / 8)
+ return new HmacSha1Association(handle, secret, remainingLifeLength);
+ if (secret.Length == CryptUtil.Sha256.HashSize / 8)
+ return new HmacSha256Association(handle, secret, remainingLifeLength);
+ throw new ArgumentException(Strings.BadAssociationPrivateData, "privateData");
+ }
protected HashAlgorithm Hasher { get; private set; }
static TimeSpan minimumUsefulAssociationLifetime {
@@ -31,7 +64,7 @@ namespace DotNetOpenId {
/// <summary>
/// A unique handle by which this <see cref="Association"/> may be stored or retrieved.
/// </summary>
- internal string Handle { get; set; }
+ public string Handle { get; private set; }
/// <summary>
/// Gets the time that this <see cref="Association"/> was first created
/// and the <see cref="SecretKey"/> issued.
@@ -49,6 +82,29 @@ namespace DotNetOpenId {
protected internal byte[] SecretKey { get; private set; }
/// <summary>
+ /// Returns private data required to persist this <see cref="Association"/> in
+ /// permanent storage (a shared database for example) for deserialization later.
+ /// </summary>
+ /// <returns>
+ /// An opaque byte array that must be stored and returned exactly as it is provided here.
+ /// The byte array may vary in length depending on the specific type of <see cref="Association"/>,
+ /// but in current versions are no larger than 256 bytes.
+ /// </returns>
+ /// <remarks>
+ /// Values of public properties on the base class <see cref="Association"/> are not included
+ /// in this byte array, as they are useful for fast database lookup and are persisted separately.
+ /// </remarks>
+ public byte[] SerializePrivateData() {
+ // 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.
+ // For now, we just send out the secret key. We can derive the type from the length later.
+ byte[] secretKeyCopy = new byte[SecretKey.Length];
+ SecretKey.CopyTo(secretKeyCopy, 0);
+ return secretKeyCopy;
+ }
+
+ /// <summary>
/// Gets the time when this <see cref="Association"/> will expire.
/// </summary>
public DateTime Expires {
diff --git a/src/DotNetOpenId/Associations.cs b/src/DotNetOpenId/Associations.cs
index 09fa29d..961c7f2 100644
--- a/src/DotNetOpenId/Associations.cs
+++ b/src/DotNetOpenId/Associations.cs
@@ -32,7 +32,7 @@ namespace DotNetOpenId {
}
/// <summary>
- /// Returns the <see cref="Association"/> with the given handle.
+ /// Returns the <see cref="Association"/> with the given handle. Null if not found.
/// </summary>
public Association Get(string handle) {
lock (this) {
@@ -53,7 +53,7 @@ namespace DotNetOpenId {
}
/// <summary>
- /// Gets the <see cref="Association"/> issued most recently.
+ /// Gets the <see cref="Association"/> issued most recently. Null if no valid associations exist.
/// </summary>
public Association Best {
get {
diff --git a/src/DotNetOpenId/IAssociationStore.cs b/src/DotNetOpenId/IAssociationStore.cs
index b5f9c5e..585514d 100644
--- a/src/DotNetOpenId/IAssociationStore.cs
+++ b/src/DotNetOpenId/IAssociationStore.cs
@@ -20,13 +20,29 @@ namespace DotNetOpenId {
/// </typeparam>
public interface IAssociationStore<TKey> {
void StoreAssociation(TKey distinguishingFactor, Association assoc);
+ /// <summary>
+ /// Gets the best association (the one with the longest remaining life) for a given key.
+ /// Null if no unexpired <see cref="Association"/>s exist for the given key.
+ /// </summary>
Association GetAssociation(TKey distinguishingFactor);
+ /// <summary>
+ /// Gets the association for a given key and handle.
+ /// Null if no unexpired <see cref="Association"/>s exist for the given key and handle.
+ /// </summary>
Association GetAssociation(TKey distinguishingFactor, string handle);
+ /// <summary>Removes a specified handle that may exist in the store.</summary>
+ /// <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 RemoveAssociation(TKey distinguishingFactor, string handle);
/// <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>
diff --git a/src/DotNetOpenId/RelyingParty/INonceStore.cs b/src/DotNetOpenId/RelyingParty/INonceStore.cs
index a4f6f25..e5d9af4 100644
--- a/src/DotNetOpenId/RelyingParty/INonceStore.cs
+++ b/src/DotNetOpenId/RelyingParty/INonceStore.cs
@@ -4,11 +4,37 @@ using System.Text;
namespace DotNetOpenId.RelyingParty {
public interface INonceStore {
+ /// <summary>
+ /// Gets some key that can be used for signing. Any positive length can be used, but a
+ /// length of 64 bytes is recommended.
+ /// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
byte[] SecretSigningKey { get; }
+ /// <summary>
+ /// Stores a nonce. Checking for an existing nonce with the same <see cref="Nonce.Code"/> value
+ /// is not necessary as <see cref="ContainsNonce"/> is atomically checked first.
+ /// </summary>
+ /// <remarks>
+ /// When persisting nonce instances, only the <see cref="Code"/> and <see cref="ExpirationDate"/>
+ /// properties are significant. Nonces never need to be deserialized.
+ /// </remarks>
void StoreNonce(Nonce nonce);
+ /// <summary>
+ /// Gets whether a given nonce already exists in the store.
+ /// </summary>
+ /// <remarks>
+ /// When checking a persistent store for an existing nonce, only compare the
+ /// <see cref="Nonce.Code"/> fields.
+ /// </remarks>
bool ContainsNonce(Nonce nonce);
+ /// <summary>
+ /// Hints to the store to clear expired nonces.
+ /// </summary>
+ /// <remarks>
+ /// If another algorithm is in place to periodically clear out expired nonces,
+ /// this method call may be ignored.
+ /// </remarks>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Nonces")]
void ClearExpiredNonces();
}
diff --git a/src/DotNetOpenId/Strings.Designer.cs b/src/DotNetOpenId/Strings.Designer.cs
index 4087a3d..3104227 100644
--- a/src/DotNetOpenId/Strings.Designer.cs
+++ b/src/DotNetOpenId/Strings.Designer.cs
@@ -61,6 +61,15 @@ namespace DotNetOpenId {
}
/// <summary>
+ /// Looks up a localized string similar to The private data supplied does not meet the requirements of any known Association type. Its length may be too short, or it may have been corrupted..
+ /// </summary>
+ internal static string BadAssociationPrivateData {
+ get {
+ return ResourceManager.GetString("BadAssociationPrivateData", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to HttpContext.Current is null. There must be an ASP.NET request in process for this operation to succeed..
/// </summary>
internal static string CurrentHttpContextRequired {
diff --git a/src/DotNetOpenId/Strings.resx b/src/DotNetOpenId/Strings.resx
index bc651cb..837a5d2 100644
--- a/src/DotNetOpenId/Strings.resx
+++ b/src/DotNetOpenId/Strings.resx
@@ -117,6 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
+ <data name="BadAssociationPrivateData" xml:space="preserve">
+ <value>The private data supplied does not meet the requirements of any known Association type. Its length may be too short, or it may have been corrupted.</value>
+ </data>
<data name="CurrentHttpContextRequired" xml:space="preserve">
<value>HttpContext.Current is null. There must be an ASP.NET request in process for this operation to succeed.</value>
</data>