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;
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
*
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,
/**
* 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:
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());
if(td == null){
return;
}
+ td = HibernateProxyHelper.deproxy(td);
deleteCandidates.add(new CdmAuthority(td, UPDATE_DELETE));
addDeleteCandidates(deleteCandidates, td.getCitation());
if(td instanceof SpecimenTypeDesignation){
if(deriveUnit == null){
return;
}
+
+ deriveUnit = HibernateProxyHelper.deproxy(deriveUnit);
if(deriveUnit.getCollection() != null){
deleteCandidates.add(new CdmAuthority(deriveUnit.getCollection(), UPDATE_DELETE));
}
if(fieldUnit == null){
return;
}
+ fieldUnit = HibernateProxyHelper.deproxy(fieldUnit);
if(fieldUnit.getGatheringEvent() != null){
addDeleteCandidates(deleteCandidates, fieldUnit.getGatheringEvent().getActor());
}
if(reference == null){
return;
}
+ reference = HibernateProxyHelper.deproxy(reference);
deleteCandidates.add(new CdmAuthority(reference, UPDATE_DELETE));
addDeleteCandidates(deleteCandidates, reference.getAuthorship());
addDeleteCandidates(deleteCandidates, reference.getInReference());
if(agent == null){
return;
}
+ agent = HibernateProxyHelper.deproxy(agent);
deleteCandidates.add(new CdmAuthority(agent, UPDATE_DELETE));
if(agent instanceof TeamOrPersonBase){
if(agent instanceof Team){
*/
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();
}