From 05071ce3b351a4b9a99f3ed1fd901855a1d68d48 Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Fri, 24 Sep 2010 08:54:01 -0400 Subject: [PATCH 1/4] Fix for orphaned EntityReference when entity is detached from all EntitiesGroups --- .../AuthorizationRepositoryFixture.cs | 48 +++++++++++++++++++ .../Services/AuthorizationRepository.cs | 9 ++++ 2 files changed, 57 insertions(+) diff --git a/Rhino.Security.Tests/AuthorizationRepositoryFixture.cs b/Rhino.Security.Tests/AuthorizationRepositoryFixture.cs index 41c2dcb..83374d6 100644 --- a/Rhino.Security.Tests/AuthorizationRepositoryFixture.cs +++ b/Rhino.Security.Tests/AuthorizationRepositoryFixture.cs @@ -5,6 +5,7 @@ namespace Rhino.Security.Tests { using System; + using NHibernate.Criterion; public class AuthorizationRepositoryFixture : DatabaseFixture { @@ -806,5 +807,52 @@ public void RemovingUserWillAlsoRemoveAssociatedPermissions() session.Delete(user); } + + [Fact] + public void DetachingEntityFromGroupDoesNotOrphanEntityReference() { + // Create new account and associate it with two groups + Account account = new Account() { Name = "JohnDoe" }; + session.Save(account); + + authorizationRepository.AssociateEntityWith(account, "Important Accounts"); + authorizationRepository.CreateEntitiesGroup("Very Important Accounts"); + authorizationRepository.AssociateEntityWith(account, "Very Important Accounts"); + session.Flush(); + + Guid key = Security.ExtractKey(account); + + // Confirm EntityReference for the new account exists. + EntityReference reference = session.CreateCriteria() + .Add(Restrictions.Eq("EntitySecurityKey", key)) + .SetCacheable(true) + .UniqueResult(); + + Assert.True(reference != null); + + // Detach the account from one group and check if its + // EntityReference still exists. + authorizationRepository.DetachEntityFromGroup(account, "Important Accounts"); + session.Flush(); + + reference = session.CreateCriteria() + .Add(Restrictions.Eq("EntitySecurityKey", key)) + .SetCacheable(true) + .UniqueResult(); + + Assert.True(reference != null); + + + // Detach the account from the other group and check + // if its EntityReference was removed. + authorizationRepository.DetachEntityFromGroup(account, "Important Accounts"); + session.Flush(); + + reference = session.CreateCriteria() + .Add(Restrictions.Eq("EntitySecurityKey", key)) + .SetCacheable(true) + .UniqueResult(); + + Assert.True(reference == null); + } } } \ No newline at end of file diff --git a/Rhino.Security/Services/AuthorizationRepository.cs b/Rhino.Security/Services/AuthorizationRepository.cs index b37f484..26459b7 100644 --- a/Rhino.Security/Services/AuthorizationRepository.cs +++ b/Rhino.Security/Services/AuthorizationRepository.cs @@ -501,6 +501,7 @@ public void DetachEntityFromGroup(TEntity entity, string entitiesGroupN EntityReference reference = GetOrCreateEntityReference(key); entitiesGroup.Entities.Remove(reference); + RemoveOrphanedEntityReference(entity, reference); } @@ -588,5 +589,13 @@ private EntityType GetOrCreateEntityType() } return entityType; } + + private void RemoveOrphanedEntityReference(TEntity entity, EntityReference reference) where TEntity : class + { + EntitiesGroup[] entitiesGroups = this.GetAssociatedEntitiesGroupsFor(entity); + if (entitiesGroups.Length <= 1) { + session.Delete(reference); + } + } } } From 360108b9d198e9215ef7dbd08e6eb15ba37c932a Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Tue, 28 Sep 2010 10:20:48 -0400 Subject: [PATCH 2/4] Undo previous fix; removed the RemoveOrphanedEntityReference method from AuthorizationRepository - Will use event listener to fix bug --- .../AuthorizationRepositoryFixture.cs | 48 ------------------- .../Services/AuthorizationRepository.cs | 9 ---- 2 files changed, 57 deletions(-) diff --git a/Rhino.Security.Tests/AuthorizationRepositoryFixture.cs b/Rhino.Security.Tests/AuthorizationRepositoryFixture.cs index 83374d6..41c2dcb 100644 --- a/Rhino.Security.Tests/AuthorizationRepositoryFixture.cs +++ b/Rhino.Security.Tests/AuthorizationRepositoryFixture.cs @@ -5,7 +5,6 @@ namespace Rhino.Security.Tests { using System; - using NHibernate.Criterion; public class AuthorizationRepositoryFixture : DatabaseFixture { @@ -807,52 +806,5 @@ public void RemovingUserWillAlsoRemoveAssociatedPermissions() session.Delete(user); } - - [Fact] - public void DetachingEntityFromGroupDoesNotOrphanEntityReference() { - // Create new account and associate it with two groups - Account account = new Account() { Name = "JohnDoe" }; - session.Save(account); - - authorizationRepository.AssociateEntityWith(account, "Important Accounts"); - authorizationRepository.CreateEntitiesGroup("Very Important Accounts"); - authorizationRepository.AssociateEntityWith(account, "Very Important Accounts"); - session.Flush(); - - Guid key = Security.ExtractKey(account); - - // Confirm EntityReference for the new account exists. - EntityReference reference = session.CreateCriteria() - .Add(Restrictions.Eq("EntitySecurityKey", key)) - .SetCacheable(true) - .UniqueResult(); - - Assert.True(reference != null); - - // Detach the account from one group and check if its - // EntityReference still exists. - authorizationRepository.DetachEntityFromGroup(account, "Important Accounts"); - session.Flush(); - - reference = session.CreateCriteria() - .Add(Restrictions.Eq("EntitySecurityKey", key)) - .SetCacheable(true) - .UniqueResult(); - - Assert.True(reference != null); - - - // Detach the account from the other group and check - // if its EntityReference was removed. - authorizationRepository.DetachEntityFromGroup(account, "Important Accounts"); - session.Flush(); - - reference = session.CreateCriteria() - .Add(Restrictions.Eq("EntitySecurityKey", key)) - .SetCacheable(true) - .UniqueResult(); - - Assert.True(reference == null); - } } } \ No newline at end of file diff --git a/Rhino.Security/Services/AuthorizationRepository.cs b/Rhino.Security/Services/AuthorizationRepository.cs index 26459b7..b37f484 100644 --- a/Rhino.Security/Services/AuthorizationRepository.cs +++ b/Rhino.Security/Services/AuthorizationRepository.cs @@ -501,7 +501,6 @@ public void DetachEntityFromGroup(TEntity entity, string entitiesGroupN EntityReference reference = GetOrCreateEntityReference(key); entitiesGroup.Entities.Remove(reference); - RemoveOrphanedEntityReference(entity, reference); } @@ -589,13 +588,5 @@ private EntityType GetOrCreateEntityType() } return entityType; } - - private void RemoveOrphanedEntityReference(TEntity entity, EntityReference reference) where TEntity : class - { - EntitiesGroup[] entitiesGroups = this.GetAssociatedEntitiesGroupsFor(entity); - if (entitiesGroups.Length <= 1) { - session.Delete(reference); - } - } } } From e8ac66754dfc7fdfb9985e25143325e377f51351 Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Tue, 28 Sep 2010 10:33:29 -0400 Subject: [PATCH 3/4] Event listener to delete security information when an entity is deleted from the system. --- .../DeleteEntityEventListenerFixture.cs | 39 +++++++++++++ .../Rhino.Security.Tests-vs2008.csproj | 1 + Rhino.Security/DeleteEntityEventListener.cs | 47 +++++++++++++++ Rhino.Security/Rhino.Security-vs2008.csproj | 1 + Rhino.Security/Security.cs | 57 +++++++++++++++---- 5 files changed, 133 insertions(+), 12 deletions(-) create mode 100644 Rhino.Security.Tests/DeleteEntityEventListenerFixture.cs create mode 100644 Rhino.Security/DeleteEntityEventListener.cs diff --git a/Rhino.Security.Tests/DeleteEntityEventListenerFixture.cs b/Rhino.Security.Tests/DeleteEntityEventListenerFixture.cs new file mode 100644 index 0000000..d9f1144 --- /dev/null +++ b/Rhino.Security.Tests/DeleteEntityEventListenerFixture.cs @@ -0,0 +1,39 @@ +namespace Rhino.Security.Tests +{ + using System; + using NHibernate.Criterion; + using Rhino.Security.Model; + using Xunit; + + public class DeleteEntityEventListenerFixture : DatabaseFixture + { + [Fact] + public void DoesDeletingEntityRemoveEntityReferences() { + var account = new Account() { Name = "Bob" }; + session.Save(account); + authorizationRepository.AssociateEntityWith(account, "Important Accounts"); + session.Flush(); + + Guid securityKey = Security.ExtractKey(account); + + // Confirm EntityReference for the new account exists. + EntityReference reference = session.CreateCriteria() + .Add(Restrictions.Eq("EntitySecurityKey", securityKey)) + .SetCacheable(true) + .UniqueResult(); + + Assert.NotNull(reference); + + // Delete account and confirm EntityReference was removed + session.Delete(account); + session.Flush(); + + reference = session.CreateCriteria() + .Add(Restrictions.Eq("EntitySecurityKey", securityKey)) + .SetCacheable(true) + .UniqueResult(); + + Assert.Null(reference); + } + } +} diff --git a/Rhino.Security.Tests/Rhino.Security.Tests-vs2008.csproj b/Rhino.Security.Tests/Rhino.Security.Tests-vs2008.csproj index 6b276e4..0ca58de 100644 --- a/Rhino.Security.Tests/Rhino.Security.Tests-vs2008.csproj +++ b/Rhino.Security.Tests/Rhino.Security.Tests-vs2008.csproj @@ -91,6 +91,7 @@ + diff --git a/Rhino.Security/DeleteEntityEventListener.cs b/Rhino.Security/DeleteEntityEventListener.cs new file mode 100644 index 0000000..5b056aa --- /dev/null +++ b/Rhino.Security/DeleteEntityEventListener.cs @@ -0,0 +1,47 @@ +namespace Rhino.Security +{ + using System; + using NHibernate.Event; + using Microsoft.Practices.ServiceLocation; + using Rhino.Security.Interfaces; + using System.Collections.Generic; + using Rhino.Security.Model; + using Rhino.Security.Services; + using NHibernate.Criterion; + + /// + /// Litenens for when a secured entity is deleted from the system and deletes + /// associated security data. + /// + [Serializable] + public class DeleteEntityEventListener : IPreDeleteEventListener + { + #region IPreDeleteEventListener Members + + /// + /// Handles PreDelete event to delete an entity's associated security data. + /// + /// Event object containing the delete operation information. + /// False, indicating the delete operation should not be vetoed. + public bool OnPreDelete(PreDeleteEvent deleteEvent) { + var securityKey = Security.ExtractKey(deleteEvent.Entity); + + if (!Guid.Empty.Equals(securityKey)) { + EntityReference reference = deleteEvent.Session.CreateCriteria() + .Add(Restrictions.Eq("EntitySecurityKey", securityKey)) + .SetCacheable(true) + .UniqueResult(); + + if (reference != null) { + var childSession = deleteEvent.Session.GetSession(NHibernate.EntityMode.Poco); + childSession.Delete(reference); + childSession.Flush(); + } + } + + return false; + } + + #endregion + } +} diff --git a/Rhino.Security/Rhino.Security-vs2008.csproj b/Rhino.Security/Rhino.Security-vs2008.csproj index 153bffc..8f1a76e 100644 --- a/Rhino.Security/Rhino.Security-vs2008.csproj +++ b/Rhino.Security/Rhino.Security-vs2008.csproj @@ -161,6 +161,7 @@ +