diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2008-11-12 17:13:15 -0800 |
---|---|---|
committer | Andrew <andrewarnott@gmail.com> | 2008-11-12 17:13:15 -0800 |
commit | 6c26c72a01e8ae62474f78e1d44b849c673f8e4e (patch) | |
tree | 3ed9e1a0ff4f39b65aace790df3456f8ce7e9867 | |
parent | 2855865d962d49c6e0145efd03e60fd7b6ea7cab (diff) | |
download | DotNetOpenAuth-6c26c72a01e8ae62474f78e1d44b849c673f8e4e.zip DotNetOpenAuth-6c26c72a01e8ae62474f78e1d44b849c673f8e4e.tar.gz DotNetOpenAuth-6c26c72a01e8ae62474f78e1d44b849c673f8e4e.tar.bz2 |
Added the associations collection and memory store.
-rw-r--r-- | src/DotNetOpenAuth/DotNetOpenAuth.csproj | 2 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OpenId/AssociationMemoryStore.cs | 114 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OpenId/Associations.cs | 99 |
3 files changed, 215 insertions, 0 deletions
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index 8c26a46..5c74ae3 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -153,6 +153,8 @@ <Compile Include="OAuth\ChannelElements\OAuthServiceProviderMessageTypeProvider.cs" /> <Compile Include="Messaging\ProtocolException.cs" /> <Compile Include="OpenId\Association.cs" /> + <Compile Include="OpenId\AssociationMemoryStore.cs" /> + <Compile Include="OpenId\Associations.cs" /> <Compile Include="OpenId\ChannelElements\ITamperResistantOpenIdMessage.cs" /> <Compile Include="OpenId\ChannelElements\SigningBindingElement.cs" /> <Compile Include="OpenId\ChannelElements\KeyValueFormEncoding.cs" /> diff --git a/src/DotNetOpenAuth/OpenId/AssociationMemoryStore.cs b/src/DotNetOpenAuth/OpenId/AssociationMemoryStore.cs new file mode 100644 index 0000000..32d2067 --- /dev/null +++ b/src/DotNetOpenAuth/OpenId/AssociationMemoryStore.cs @@ -0,0 +1,114 @@ +//----------------------------------------------------------------------- +// <copyright file="AssociationMemoryStore.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OpenId { + using System.Collections.Generic; + + /// <summary> + /// Manages a set of associations in memory only (no database). + /// </summary> + /// <typeparam name="TKey">The type of the key.</typeparam> + /// <remarks> + /// This class should be used for low-to-medium traffic relying party sites that can afford to lose associations + /// if the app pool was ever restarted. High traffic relying parties and providers should write their own + /// implementation of <see cref="IAssociationStore<TKey>"/> that works against their own database schema + /// to allow for persistance and recall of associations across servers in a web farm and server restarts. + /// </remarks> + internal class AssociationMemoryStore<TKey> : IAssociationStore<TKey> { + /// <summary> + /// For Relying Parties, this maps OP Endpoints to a set of associations with that endpoint. + /// For Providers, this keeps smart and dumb associations in two distinct pools. + /// </summary> + private Dictionary<TKey, Associations> serverAssocsTable = new Dictionary<TKey, Associations>(); + + /// <summary> + /// Stores a given association for later recall. + /// </summary> + /// <param name="distinguishingFactor">The distinguishing factor, either an OP Endpoint or smart/dumb mode.</param> + /// <param name="assoc">The association to store.</param> + public void StoreAssociation(TKey distinguishingFactor, Association assoc) { + lock (this) { + if (!this.serverAssocsTable.ContainsKey(distinguishingFactor)) { + this.serverAssocsTable.Add(distinguishingFactor, new Associations()); + } + Associations server_assocs = this.serverAssocsTable[distinguishingFactor]; + + server_assocs.Set(assoc); + } + } + + /// <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> + /// <returns> + /// The requested association, or null if no unexpired <see cref="Association"/>s exist for the given key. + /// </returns> + public Association GetAssociation(TKey distinguishingFactor) { + lock (this) { + return this.GetServerAssociations(distinguishingFactor).Best; + } + } + + /// <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> + public Association GetAssociation(TKey distinguishingFactor, string handle) { + lock (this) { + return this.GetServerAssociations(distinguishingFactor).Get(handle); + } + } + + /// <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> + public bool RemoveAssociation(TKey distinguishingFactor, string handle) { + lock (this) { + return this.GetServerAssociations(distinguishingFactor).Remove(handle); + } + } + + /// <summary> + /// Clears all expired associations from the store. + /// </summary> + public void ClearExpiredAssociations() { + lock (this) { + foreach (Associations assocs in this.serverAssocsTable.Values) { + assocs.ClearExpired(); + } + } + } + + /// <summary> + /// Gets the server associations for a given OP Endpoint or dumb/smart mode. + /// </summary> + /// <param name="distinguishingFactor">The distinguishing factor, either an OP Endpoint (for relying parties) or smart/dumb (for providers).</param> + /// <returns>The collection of associations that fit the <paramref name="distinguishingFactor"/>.</returns> + internal Associations GetServerAssociations(TKey distinguishingFactor) { + lock (this) { + if (!this.serverAssocsTable.ContainsKey(distinguishingFactor)) { + this.serverAssocsTable.Add(distinguishingFactor, new Associations()); + } + + return this.serverAssocsTable[distinguishingFactor]; + } + } + } +} diff --git a/src/DotNetOpenAuth/OpenId/Associations.cs b/src/DotNetOpenAuth/OpenId/Associations.cs new file mode 100644 index 0000000..064d8f0 --- /dev/null +++ b/src/DotNetOpenAuth/OpenId/Associations.cs @@ -0,0 +1,99 @@ +//----------------------------------------------------------------------- +// <copyright file="Associations.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OpenId { + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Linq; + using DotNetOpenAuth.Messaging; + + /// <summary> + /// A dictionary of handle/Association pairs. + /// </summary> + /// <remarks> + /// Each method is locked, even if it is only one line, so that they are thread safe + /// against each other, particularly the ones that enumerate over the list, since they + /// can break if the collection is changed by another thread during enumeration. + /// </remarks> + [DebuggerDisplay("Count = {assocs.Count}")] + internal class Associations { + /// <summary> + /// The lookup table where keys are the association handles and values are the associations themselves. + /// </summary> + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + private readonly Dictionary<string, Association> associations; + + /// <summary> + /// Initializes a new instance of the <see cref="Associations"/> class. + /// </summary> + public Associations() { + this.associations = new Dictionary<string, Association>(); + } + + /// <summary> + /// Gets the <see cref="Association"/> issued most recently. Null if no valid associations exist. + /// </summary> + /// <remarks> + /// This property is used by relying parties that are initiating authentication requests. + /// It does not apply to Providers, which always need a specific association by handle. + /// </remarks> + public Association Best { + get { + lock (this.associations) { + return this.associations.Values.OrderByDescending(assoc => assoc.Issued).FirstOrDefault(); + } + } + } + + /// <summary> + /// Stores an <see cref="Association"/> in the collection. + /// </summary> + /// <param name="association">The association to add to the collection.</param> + public void Set(Association association) { + ErrorUtilities.VerifyArgumentNotNull(association, "association"); + lock (this.associations) { + this.associations[association.Handle] = association; + } + } + + /// <summary> + /// Returns the <see cref="Association"/> with the given handle. Null if not found. + /// </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> + public Association Get(string handle) { + lock (this.associations) { + Association assoc; + this.associations.TryGetValue(handle, out assoc); + return assoc; + } + } + + /// <summary> + /// Removes the <see cref="Association"/> with the given handle. + /// </summary> + /// <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) { + lock (this.associations) { + return this.associations.Remove(handle); + } + } + + /// <summary> + /// Removes all expired associations from the collection. + /// </summary> + public void ClearExpired() { + lock (this.associations) { + var expireds = this.associations.Values.Where(assoc => assoc.IsExpired).ToList(); + foreach (Association assoc in expireds) { + this.associations.Remove(assoc.Handle); + } + } + } + } +} |