summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2008-11-12 17:13:15 -0800
committerAndrew <andrewarnott@gmail.com>2008-11-12 17:13:15 -0800
commit6c26c72a01e8ae62474f78e1d44b849c673f8e4e (patch)
tree3ed9e1a0ff4f39b65aace790df3456f8ce7e9867
parent2855865d962d49c6e0145efd03e60fd7b6ea7cab (diff)
downloadDotNetOpenAuth-6c26c72a01e8ae62474f78e1d44b849c673f8e4e.zip
DotNetOpenAuth-6c26c72a01e8ae62474f78e1d44b849c673f8e4e.tar.gz
DotNetOpenAuth-6c26c72a01e8ae62474f78e1d44b849c673f8e4e.tar.bz2
Added the associations collection and memory store.
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj2
-rw-r--r--src/DotNetOpenAuth/OpenId/AssociationMemoryStore.cs114
-rw-r--r--src/DotNetOpenAuth/OpenId/Associations.cs99
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&lt;TKey&gt;"/> 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);
+ }
+ }
+ }
+ }
+}