adapt vaadin to new package structure for persistence.permission
[cdm-vaadin.git] / src / main / java / eu / etaxonomy / cdm / persistence / hibernate / GrantedAuthorityRevokingRegistrationUpdateLister.java
index 0e12923fcf78c8c4fa53e4c7d26a2c86c6c8ae59..42a7cb1c35700d686cd5a428b84eda161df59073 100644 (file)
@@ -14,14 +14,19 @@ import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import org.hibernate.FlushMode;
 import org.hibernate.Query;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
 import org.hibernate.event.spi.EventSource;
 import org.hibernate.event.spi.PostUpdateEvent;
 import org.hibernate.event.spi.PostUpdateEventListener;
 import org.hibernate.persister.entity.EntityPersister;
+import org.springframework.security.core.GrantedAuthority;
 
+import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
 import eu.etaxonomy.cdm.model.agent.AgentBase;
 import eu.etaxonomy.cdm.model.agent.Person;
 import eu.etaxonomy.cdm.model.agent.Team;
@@ -34,11 +39,25 @@ import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
 import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
 import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
+import eu.etaxonomy.cdm.model.permission.CRUD;
+import eu.etaxonomy.cdm.model.permission.Group;
+import eu.etaxonomy.cdm.model.permission.User;
 import eu.etaxonomy.cdm.model.reference.Reference;
-import eu.etaxonomy.cdm.persistence.hibernate.permission.CRUD;
-import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmAuthority;
+import eu.etaxonomy.cdm.persistence.permission.CdmAuthority;
 
 /**
+ * This Hibernate {@link PostUpdateEventListener} is responsible for
+ * revoking GrantedAuthorities from any user which is having per entity
+ * permissions in the object graph of the `Registration`being updated
+ * This encompasses GrantedAuthotities with the CRUD values CRUD.UPDATE, CRUD.DELETE.
+ * Please refer to the method documentation of {@link #collectDeleteCandidates(Registration)}
+ * for further details.
+ * <p>
+ * The according permissions are revoked when the RegistrationStatus is being changed
+ * by a database update. The RegistrationStatus causing this are contained in the constant
+ * {@link GrantedAuthorityRevokingRegistrationUpdateLister#MODIFICATION_STOP_STATES MODIFICATION_STOP_STATES}
+ *
+ *
  * @author a.kohlbecker
  * @since Dec 18, 2017
  *
@@ -48,9 +67,8 @@ public class GrantedAuthorityRevokingRegistrationUpdateLister implements PostUpd
     private static final long serialVersionUID = -3542204523291766866L;
 
     /**
-     *
-     * Registrations having these states must no longer be midifiable by users having only per entity permissions on the
-     * Registration subgraph
+     * Registrations having these states must no longer be modifiable by users having
+     * only per entity permissions on the Registration subgraph.
      */
     private static final EnumSet<RegistrationStatus> MODIFICATION_STOP_STATES = EnumSet.of(
             RegistrationStatus.PUBLISHED,
@@ -79,8 +97,9 @@ public class GrantedAuthorityRevokingRegistrationUpdateLister implements PostUpd
     /**
      * Walks the entity graph of the Registration instance and collects all authorities which
      * could have been granted to users. Code parts in which this could have happened can be
-     * found by searching for usage of the methods {@link eu.etaxonomy.cdm.vaadin.security.UserHelper#createAuthorityForCurrentUser(eu.etaxonomy.cdm.model.common.CdmBase, EnumSet, String)
-     * UserHelper.createAuthorityForCurrentUser(eu.etaxonomy.cdm.model.common.CdmBase, EnumSet, String)} and {@link eu.etaxonomy.cdm.vaadin.security.UserHelper#createAuthorityForCurrentUser(Class, Integer, EnumSet, String)
+     * found by searching for usage of the methods {@link eu.etaxonomy.cdm.api.utility.UserHelper#createAuthorityForCurrentUser(eu.etaxonomy.cdm.model.common.CdmBase, EnumSet, String)
+     * UserHelper.createAuthorityForCurrentUser(eu.etaxonomy.cdm.model.common.CdmBase, EnumSet, String)} and
+     * {@link eu.etaxonomy.cdm.api.utility.UserHelper#createAuthorityForCurrentUser(Class, Integer, EnumSet, String)
      * UserHelper.createAuthorityForCurrentUser(Class, Integer, EnumSet, String)}
      * <p>
      * At the time of implementing this function these places are:
@@ -121,8 +140,9 @@ public class GrantedAuthorityRevokingRegistrationUpdateLister implements PostUpd
         if(name == null){
             return;
         }
+        name = HibernateProxyHelper.deproxy(name);
         deleteCandidates.add(new CdmAuthority(name, UPDATE_DELETE));
-        addDeleteCandidates(deleteCandidates, (Reference)name.getNomenclaturalReference());
+        addDeleteCandidates(deleteCandidates, name.getNomenclaturalReference());
         addDeleteCandidates(deleteCandidates, name.getCombinationAuthorship());
         addDeleteCandidates(deleteCandidates, name.getExCombinationAuthorship());
         addDeleteCandidates(deleteCandidates, name.getBasionymAuthorship());
@@ -138,6 +158,7 @@ public class GrantedAuthorityRevokingRegistrationUpdateLister implements PostUpd
         if(td == null){
             return;
         }
+        td = HibernateProxyHelper.deproxy(td);
         deleteCandidates.add(new CdmAuthority(td, UPDATE_DELETE));
         addDeleteCandidates(deleteCandidates, td.getCitation());
         if(td instanceof SpecimenTypeDesignation){
@@ -154,6 +175,8 @@ public class GrantedAuthorityRevokingRegistrationUpdateLister implements PostUpd
         if(deriveUnit == null){
             return;
         }
+
+        deriveUnit = HibernateProxyHelper.deproxy(deriveUnit);
         if(deriveUnit.getCollection() != null){
             deleteCandidates.add(new CdmAuthority(deriveUnit.getCollection(), UPDATE_DELETE));
         }
@@ -174,6 +197,7 @@ public class GrantedAuthorityRevokingRegistrationUpdateLister implements PostUpd
         if(fieldUnit == null){
             return;
         }
+        fieldUnit = HibernateProxyHelper.deproxy(fieldUnit);
         if(fieldUnit.getGatheringEvent() != null){
             addDeleteCandidates(deleteCandidates, fieldUnit.getGatheringEvent().getActor());
         }
@@ -187,6 +211,7 @@ public class GrantedAuthorityRevokingRegistrationUpdateLister implements PostUpd
         if(reference == null){
             return;
         }
+        reference = HibernateProxyHelper.deproxy(reference);
         deleteCandidates.add(new CdmAuthority(reference, UPDATE_DELETE));
         addDeleteCandidates(deleteCandidates, reference.getAuthorship());
         addDeleteCandidates(deleteCandidates, reference.getInReference());
@@ -196,6 +221,7 @@ public class GrantedAuthorityRevokingRegistrationUpdateLister implements PostUpd
         if(agent == null){
             return;
         }
+        agent = HibernateProxyHelper.deproxy(agent);
         deleteCandidates.add(new CdmAuthority(agent, UPDATE_DELETE));
         if(agent instanceof TeamOrPersonBase){
             if(agent instanceof Team){
@@ -220,14 +246,55 @@ public class GrantedAuthorityRevokingRegistrationUpdateLister implements PostUpd
      */
     private void deleteAuthorities(EventSource session, Set<CdmAuthority> deleteCandidates) {
 
+        if(deleteCandidates.isEmpty()){
+            return;
+        }
+
         Collection<String> authorityStrings = new ArrayList<String>(deleteCandidates.size());
         deleteCandidates.forEach( dc -> authorityStrings.add(dc.toString()));
 
+        // -----------------------------------------------------------------------------------------
+        // this needs to be executed in a separate session to avoid concurrent modification problems
+        // See also TaxonGraphHibernateListener for a Listener with also a temporary sub-session
+        Session newSession = session.getSessionFactory().openSession();
+        try {
+            Transaction txState = newSession.beginTransaction();
+
+            Query userQuery = newSession.createQuery("select u from User u join u.grantedAuthorities ga where ga.authority in (:authorities)");
+            userQuery.setParameterList("authorities", authorityStrings);
+            List<User> users = userQuery.list();
+            for(User user : users){
+                List<GrantedAuthority> deleteFromUser = user.getGrantedAuthorities().stream().filter(
+                            ga -> authorityStrings.contains(ga.getAuthority())
+                        )
+                        .collect(Collectors.toList());
+                user.getGrantedAuthorities().removeAll(deleteFromUser);
+            }
+
+            Query groupQuery = newSession.createQuery("select g from Group g join g.grantedAuthorities ga where ga.authority in (:authorities)");
+            groupQuery.setParameterList("authorities", authorityStrings);
+            List<Group> groups = groupQuery.list();
+            for(Group group : groups){
+                List<GrantedAuthority> deleteFromUser = group.getGrantedAuthorities().stream().filter(
+                            ga -> authorityStrings.contains(ga.getAuthority())
+                        )
+                        .collect(Collectors.toList());
+                group.getGrantedAuthorities().removeAll(deleteFromUser);
+            }
+            newSession.flush();
+            txState.commit();
+        } finally {
+            // no catching of the exception, if the session flush fails the transaction should roll back and
+            // the exception needs to bubble up so that the transaction in enclosing session is also rolled back
+            newSession.close();
+        }
+        // -----------------------------------------------------------------------------------------
+
         String hql = "delete from GrantedAuthorityImpl as ga where ga.authority in (:authorities)";
-        Query query = session.createQuery(hql);
-        query.setParameterList("authorities", authorityStrings);
-        query.setFlushMode(FlushMode.MANUAL); // workaround for  HHH-11822 (https://hibernate.atlassian.net/browse/HHH-11822)
-        query.executeUpdate();
+        Query deleteQuery = session.createQuery(hql);
+        deleteQuery.setParameterList("authorities", authorityStrings);
+        deleteQuery.setFlushMode(FlushMode.MANUAL); // workaround for  HHH-11822 (https://hibernate.atlassian.net/browse/HHH-11822)
+        deleteQuery.executeUpdate();
 
     }