/// <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> public void ClearExpiredAssociations() { using (var dataContext = new TransactedDatabaseEntities(IsolationLevel.ReadCommitted)) { dataContext.ClearExpiredAssociations(dataContext.Transaction); } }
/// <summary> /// Stores a given nonce and timestamp. /// </summary> /// <param name="context">The context, or namespace, within which the /// <paramref name="nonce"/> must be unique. /// The context SHOULD be treated as case-sensitive. /// The value will never be <c>null</c> but may be the empty string.</param> /// <param name="nonce">A series of random characters.</param> /// <param name="timestampUtc">The UTC timestamp that together with the nonce string make it unique /// within the given <paramref name="context"/>. /// The timestamp may also be used by the data store to clear out old nonces.</param> /// <returns> /// True if the context+nonce+timestamp (combination) was not previously in the database. /// False if the nonce was stored previously with the same timestamp and context. /// </returns> /// <remarks> /// The nonce must be stored for no less than the maximum time window a message may /// be processed within before being discarded as an expired message. /// This maximum message age can be looked up via the /// <see cref="DotNetOpenAuth.Configuration.MessagingElement.MaximumMessageLifetime"/> /// property, accessible via the <see cref="DotNetOpenAuth.Configuration.DotNetOpenAuthSection.Configuration"/> /// property. /// </remarks> public bool StoreNonce(string context, string nonce, DateTime timestampUtc) { try { using (var dataContext = new TransactedDatabaseEntities(IsolationLevel.ReadCommitted)) { Nonce nonceEntity = new Nonce { Context = context, Code = nonce, IssuedUtc = timestampUtc, ExpiresUtc = timestampUtc + DotNetOpenAuthSection.Configuration.Messaging.MaximumMessageLifetime, }; // The database columns [context] and [code] MUST be using // a case sensitive collation for this to be secure. dataContext.AddToNonces(nonceEntity); } } catch (UpdateException) { // A nonce collision return(false); } // Only clear nonces after successfully storing a nonce. // This mitigates cheap DoS attacks that take up a lot of // database cycles. ClearNoncesIfAppropriate(); return(true); }
/// <summary> /// Stores a given nonce and timestamp. /// </summary> /// <param name="context">The context, or namespace, within which the /// <paramref name="nonce"/> must be unique. /// The context SHOULD be treated as case-sensitive. /// The value will never be <c>null</c> but may be the empty string.</param> /// <param name="nonce">A series of random characters.</param> /// <param name="timestampUtc">The UTC timestamp that together with the nonce string make it unique /// within the given <paramref name="context"/>. /// The timestamp may also be used by the data store to clear out old nonces.</param> /// <returns> /// True if the context+nonce+timestamp (combination) was not previously in the database. /// False if the nonce was stored previously with the same timestamp and context. /// </returns> /// <remarks> /// The nonce must be stored for no less than the maximum time window a message may /// be processed within before being discarded as an expired message. /// This maximum message age can be looked up via the /// <see cref="DotNetOpenAuth.Configuration.MessagingElement.MaximumMessageLifetime"/> /// property, accessible via the <see cref="DotNetOpenAuth.Configuration.MessagingElement.Configuration"/> /// property. /// </remarks> public bool StoreNonce(string context, string nonce, DateTime timestampUtc) { try { using (var dataContext = new TransactedDatabaseEntities(IsolationLevel.ReadCommitted)) { Nonce nonceEntity = new Nonce { Context = context, Code = nonce, IssuedUtc = timestampUtc, ExpiresUtc = timestampUtc + DotNetOpenAuthSection.Messaging.MaximumMessageLifetime, }; // The database columns [context] and [code] MUST be using // a case sensitive collation for this to be secure. dataContext.AddToNonces(nonceEntity); } } catch (UpdateException) { // A nonce collision return false; } // Only clear nonces after successfully storing a nonce. // This mitigates cheap DoS attacks that take up a lot of // database cycles. ClearNoncesIfAppropriate(); return true; }
public void RemoveKey(string bucket, string handle) { using (var dataContext = new TransactedDatabaseEntities(System.Data.IsolationLevel.ReadCommitted)) { var association = dataContext.SymmetricCryptoKeys.FirstOrDefault(a => a.Bucket == bucket && a.Handle == handle); if (association != null) { dataContext.DeleteObject(association); } else { } } }
/// <summary> /// Clears the nonces if appropriate. /// </summary> private static void ClearNoncesIfAppropriate() { if (++nonceClearingCounter % NonceClearingInterval == 0) { using (var dataContext = new TransactedDatabaseEntities(IsolationLevel.ReadCommitted)) { dataContext.ClearExpiredNonces(dataContext.Transaction); } } }
public void StoreKey(string bucket, string handle, CryptoKey key) { using (var dataContext = new TransactedDatabaseEntities(System.Data.IsolationLevel.ReadCommitted)) { var sharedAssociation = new SymmetricCryptoKey { Bucket = bucket, Handle = handle, ExpirationUtc = key.ExpiresUtc, Secret = key.Key, }; dataContext.AddToSymmetricCryptoKeys(sharedAssociation); } }
public IEnumerable<KeyValuePair<string, CryptoKey>> GetKeys(string bucket) { using (var dataContext = new TransactedDatabaseEntities(System.Data.IsolationLevel.ReadCommitted)) { var relevantAssociations = from assoc in dataContext.SymmetricCryptoKeys where assoc.Bucket == bucket where assoc.ExpirationUtc > DateTime.UtcNow orderby assoc.ExpirationUtc descending select assoc; var qualifyingAssociations = relevantAssociations.AsEnumerable() .Select(assoc => new KeyValuePair<string, CryptoKey>(assoc.Handle, new CryptoKey(assoc.Secret, assoc.ExpirationUtc.AsUtc()))); return qualifyingAssociations.ToList(); // the data context is closing, so we must cache the result. } }
public CryptoKey GetKey(string bucket, string handle) { using (var dataContext = new TransactedDatabaseEntities(System.Data.IsolationLevel.ReadCommitted)) { var associations = from assoc in dataContext.SymmetricCryptoKeys where assoc.Bucket == bucket where assoc.Handle == handle where assoc.ExpirationUtc > DateTime.UtcNow select assoc; return associations.AsEnumerable() .Select(assoc => new CryptoKey(assoc.Secret, assoc.ExpirationUtc.AsUtc())) .FirstOrDefault(); } }
public CryptoKey GetKey(string bucket, string handle) { using (var dataContext = new TransactedDatabaseEntities(System.Data.IsolationLevel.ReadCommitted)) { var associations = from assoc in dataContext.SymmetricCryptoKeys where assoc.Bucket == bucket where assoc.Handle == handle where assoc.ExpirationUtc > DateTime.UtcNow select assoc; return(associations.AsEnumerable() .Select(assoc => new CryptoKey(assoc.Secret, assoc.ExpirationUtc.AsUtc())) .FirstOrDefault()); } }
/// <summary> /// Saves an <see cref="Association"/> for later recall. /// </summary> /// <param name="distinguishingFactor">The Uri (for relying parties) or Smart/Dumb (for providers).</param> /// <param name="association">The association to store.</param> /// <remarks> /// TODO: what should implementations do on association handle conflict? /// </remarks> public void StoreAssociation(Uri distinguishingFactor, Association association) { using (var dataContext = new TransactedDatabaseEntities(System.Data.IsolationLevel.ReadCommitted)) { var sharedAssociation = new OpenIdAssociation { DistinguishingFactor = distinguishingFactor.AbsoluteUri, AssociationHandle = association.Handle, ExpirationUtc = association.Expires, PrivateData = association.SerializePrivateData(), }; dataContext.AddToOpenIdAssociations(sharedAssociation); } }
public IEnumerable <KeyValuePair <string, CryptoKey> > GetKeys(string bucket) { using (var dataContext = new TransactedDatabaseEntities(System.Data.IsolationLevel.ReadCommitted)) { var relevantAssociations = from assoc in dataContext.SymmetricCryptoKeys where assoc.Bucket == bucket where assoc.ExpirationUtc > DateTime.UtcNow orderby assoc.ExpirationUtc descending select assoc; var qualifyingAssociations = relevantAssociations.AsEnumerable() .Select(assoc => new KeyValuePair <string, CryptoKey>(assoc.Handle, new CryptoKey(assoc.Secret, assoc.ExpirationUtc.AsUtc()))); return(qualifyingAssociations.ToList()); // the data context is closing, so we must cache the result. } }
/// <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(Uri distinguishingFactor, string handle) { using (var dataContext = new TransactedDatabaseEntities(System.Data.IsolationLevel.ReadCommitted)) { var associations = from assoc in dataContext.OpenIdAssociations where assoc.DistinguishingFactor == distinguishingFactor.AbsoluteUri where assoc.AssociationHandle == handle where assoc.ExpirationUtc > DateTime.UtcNow select assoc; return(associations.AsEnumerable() .Select(assoc => DeserializeAssociation(assoc)) .FirstOrDefault()); } }
/// <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(Uri distinguishingFactor, string handle) { using (var dataContext = new TransactedDatabaseEntities(System.Data.IsolationLevel.ReadCommitted)) { var association = dataContext.OpenIdAssociations.FirstOrDefault(a => a.DistinguishingFactor == distinguishingFactor.AbsoluteUri && a.AssociationHandle == handle); if (association != null) { dataContext.DeleteObject(association); return(true); } else { return(false); } } }
/// <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> /// <param name="securityRequirements">The security requirements that the returned association must meet.</param> /// <returns> /// The requested association, or null if no unexpired <see cref="Association"/>s exist for the given key. /// </returns> /// <remarks> /// In the event that multiple associations exist for the given /// <paramref name="distinguishingFactor"/>, it is important for the /// implementation for this method to use the <paramref name="securityRequirements"/> /// to pick the best (highest grade or longest living as the host's policy may dictate) /// association that fits the security requirements. /// Associations that are returned that do not meet the security requirements will be /// ignored and a new association created. /// </remarks> public Association GetAssociation(Uri distinguishingFactor, SecuritySettings securityRequirements) { using (var dataContext = new TransactedDatabaseEntities(System.Data.IsolationLevel.ReadCommitted)) { var relevantAssociations = from assoc in dataContext.OpenIdAssociations where assoc.DistinguishingFactor == distinguishingFactor.AbsoluteUri where assoc.ExpirationUtc > DateTime.UtcNow where assoc.PrivateDataLength * 8 >= securityRequirements.MinimumHashBitLength where assoc.PrivateDataLength * 8 <= securityRequirements.MaximumHashBitLength orderby assoc.ExpirationUtc descending select assoc; var qualifyingAssociations = relevantAssociations.AsEnumerable() .Select(assoc => DeserializeAssociation(assoc)); return(qualifyingAssociations.FirstOrDefault()); } }
/// <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> /// <param name="securityRequirements">The security requirements that the returned association must meet.</param> /// <returns> /// The requested association, or null if no unexpired <see cref="Association"/>s exist for the given key. /// </returns> /// <remarks> /// In the event that multiple associations exist for the given /// <paramref name="distinguishingFactor"/>, it is important for the /// implementation for this method to use the <paramref name="securityRequirements"/> /// to pick the best (highest grade or longest living as the host's policy may dictate) /// association that fits the security requirements. /// Associations that are returned that do not meet the security requirements will be /// ignored and a new association created. /// </remarks> public Association GetAssociation(Uri distinguishingFactor, SecuritySettings securityRequirements) { using (var dataContext = new TransactedDatabaseEntities(System.Data.IsolationLevel.ReadCommitted)) { var relevantAssociations = from assoc in dataContext.OpenIdAssociations where assoc.DistinguishingFactor == distinguishingFactor.AbsoluteUri where assoc.ExpirationUtc > DateTime.UtcNow where assoc.PrivateDataLength * 8 >= securityRequirements.MinimumHashBitLength where assoc.PrivateDataLength * 8 <= securityRequirements.MaximumHashBitLength orderby assoc.ExpirationUtc descending select assoc; var qualifyingAssociations = relevantAssociations.AsEnumerable() .Select(assoc => DeserializeAssociation(assoc)); return qualifyingAssociations.FirstOrDefault(); } }
/// <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> internal void ClearExpiredCryptoKeys() { using (var dataContext = new TransactedDatabaseEntities(IsolationLevel.ReadCommitted)) { dataContext.ClearExpiredCryptoKeys(dataContext.Transaction); } }
/// <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(Uri distinguishingFactor, string handle) { using (var dataContext = new TransactedDatabaseEntities(System.Data.IsolationLevel.ReadCommitted)) { var association = dataContext.OpenIdAssociations.FirstOrDefault(a => a.DistinguishingFactor == distinguishingFactor.AbsoluteUri && a.AssociationHandle == handle); if (association != null) { dataContext.DeleteObject(association); return true; } else { return false; } } }
/// <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(Uri distinguishingFactor, string handle) { using (var dataContext = new TransactedDatabaseEntities(System.Data.IsolationLevel.ReadCommitted)) { var associations = from assoc in dataContext.OpenIdAssociations where assoc.DistinguishingFactor == distinguishingFactor.AbsoluteUri where assoc.AssociationHandle == handle where assoc.ExpirationUtc > DateTime.UtcNow select assoc; return associations.AsEnumerable() .Select(assoc => DeserializeAssociation(assoc)) .FirstOrDefault(); } }