2 * Copyright (C) 2017 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
9 package eu
.etaxonomy
.cdm
.persistence
.hibernate
;
11 import java
.util
.ArrayList
;
12 import java
.util
.Collection
;
13 import java
.util
.EnumSet
;
14 import java
.util
.HashSet
;
15 import java
.util
.List
;
17 import java
.util
.stream
.Collectors
;
19 import org
.hibernate
.FlushMode
;
20 import org
.hibernate
.Query
;
21 import org
.hibernate
.Session
;
22 import org
.hibernate
.Transaction
;
23 import org
.hibernate
.event
.spi
.EventSource
;
24 import org
.hibernate
.event
.spi
.PostUpdateEvent
;
25 import org
.hibernate
.event
.spi
.PostUpdateEventListener
;
26 import org
.hibernate
.persister
.entity
.EntityPersister
;
27 import org
.springframework
.security
.core
.GrantedAuthority
;
29 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
30 import eu
.etaxonomy
.cdm
.model
.agent
.AgentBase
;
31 import eu
.etaxonomy
.cdm
.model
.agent
.Person
;
32 import eu
.etaxonomy
.cdm
.model
.agent
.Team
;
33 import eu
.etaxonomy
.cdm
.model
.agent
.TeamOrPersonBase
;
34 import eu
.etaxonomy
.cdm
.model
.common
.Group
;
35 import eu
.etaxonomy
.cdm
.model
.common
.User
;
36 import eu
.etaxonomy
.cdm
.model
.name
.Registration
;
37 import eu
.etaxonomy
.cdm
.model
.name
.RegistrationStatus
;
38 import eu
.etaxonomy
.cdm
.model
.name
.SpecimenTypeDesignation
;
39 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
40 import eu
.etaxonomy
.cdm
.model
.name
.TypeDesignationBase
;
41 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
42 import eu
.etaxonomy
.cdm
.model
.occurrence
.FieldUnit
;
43 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
44 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
45 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.permission
.CRUD
;
46 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.permission
.CdmAuthority
;
49 * @author a.kohlbecker
53 public class GrantedAuthorityRevokingRegistrationUpdateLister
implements PostUpdateEventListener
{
55 private static final long serialVersionUID
= -3542204523291766866L;
59 * Registrations having these states must no longer be modifiable by users having only per entity permissions on the
60 * Registration subgraph
62 private static final EnumSet
<RegistrationStatus
> MODIFICATION_STOP_STATES
= EnumSet
.of(
63 RegistrationStatus
.PUBLISHED
,
64 RegistrationStatus
.READY
,
65 RegistrationStatus
.REJECTED
68 private static final EnumSet
<CRUD
> UPDATE_DELETE
= EnumSet
.of(CRUD
.UPDATE
, CRUD
.DELETE
);
70 private static final EnumSet
<CRUD
> UPDATE
= EnumSet
.of(CRUD
.UPDATE
);
76 public void onPostUpdate(PostUpdateEvent event
) {
77 if( event
.getEntity() instanceof Registration
){
78 Registration reg
= (Registration
)event
.getEntity();
79 if(reg
.getStatus() != null && MODIFICATION_STOP_STATES
.contains(reg
.getStatus())){
80 Set
<CdmAuthority
> deleteCandidates
= collectDeleteCandidates(reg
);
81 deleteAuthorities(event
.getSession(), deleteCandidates
);
87 * Walks the entity graph of the Registration instance and collects all authorities which
88 * could have been granted to users. Code parts in which this could have happened can be
89 * found by searching for usage of the methods {@link eu.etaxonomy.cdm.vaadin.permission.UserHelper#createAuthorityForCurrentUser(eu.etaxonomy.cdm.model.common.CdmBase, EnumSet, String)
90 * UserHelper.createAuthorityForCurrentUser(eu.etaxonomy.cdm.model.common.CdmBase, EnumSet, String)} and {@link eu.etaxonomy.cdm.vaadin.permission.UserHelper#createAuthorityForCurrentUser(Class, Integer, EnumSet, String)
91 * UserHelper.createAuthorityForCurrentUser(Class, Integer, EnumSet, String)}
93 * At the time of implementing this function these places are:
95 * <li><code>RegistrationEditorPresenter.guaranteePerEntityCRUDPermissions(...)</code></li>
96 * <li><code>RegistrationWorkingsetPresenter.createNewRegistrationForName(Integer taxonNameId)</code></li>
97 * <li><code>TaxonNameEditorPresenter.guaranteePerEntityCRUDPermissions(...)</code></li>
98 * <li><code>ReferenceEditorPresenter.guaranteePerEntityCRUDPermissions(...)</code></li>
99 * <li><code>PersonField.commit()</code></li>
100 * <li><code>TeamOrPersonField.commit()</code></li>
101 * <li><code>SpecimenTypeDesignationWorkingsetEditorPresenter.saveBean(SpecimenTypeDesignationWorkingSetDTO dto)</code></li>
104 * @param reg the Registration
105 * @return the set of all possible CdmAuthorities that could have been granted to
108 private Set
<CdmAuthority
> collectDeleteCandidates(Registration reg
){
109 Set
<CdmAuthority
> deleteCandidates
= new HashSet
<CdmAuthority
>();
110 // add authority for Registration
111 deleteCandidates
.add(new CdmAuthority(reg
, RegistrationStatus
.PREPARATION
.name(), UPDATE
));
112 if(reg
.getName() != null){
113 addDeleteCandidates(deleteCandidates
, reg
.getName());
115 for(TypeDesignationBase td
: reg
.getTypeDesignations()){
116 addDeleteCandidates(deleteCandidates
, td
);
119 return deleteCandidates
;
124 * @param deleteCandidates
127 private void addDeleteCandidates(Set
<CdmAuthority
> deleteCandidates
, TaxonName name
) {
131 name
= HibernateProxyHelper
.deproxy(name
);
132 deleteCandidates
.add(new CdmAuthority(name
, UPDATE_DELETE
));
133 addDeleteCandidates(deleteCandidates
, name
.getNomenclaturalReference());
134 addDeleteCandidates(deleteCandidates
, name
.getCombinationAuthorship());
135 addDeleteCandidates(deleteCandidates
, name
.getExCombinationAuthorship());
136 addDeleteCandidates(deleteCandidates
, name
.getBasionymAuthorship());
137 addDeleteCandidates(deleteCandidates
, name
.getExBasionymAuthorship());
142 * @param deleteCandidates
145 private void addDeleteCandidates(Set
<CdmAuthority
> deleteCandidates
, TypeDesignationBase td
) {
149 td
= HibernateProxyHelper
.deproxy(td
);
150 deleteCandidates
.add(new CdmAuthority(td
, UPDATE_DELETE
));
151 addDeleteCandidates(deleteCandidates
, td
.getCitation());
152 if(td
instanceof SpecimenTypeDesignation
){
153 SpecimenTypeDesignation std
= (SpecimenTypeDesignation
)td
;
154 addDeleteCandidates(deleteCandidates
, std
.getTypeSpecimen());
159 * @param deleteCandidates
160 * @param typeSpecimen
162 private void addDeleteCandidates(Set
<CdmAuthority
> deleteCandidates
, DerivedUnit deriveUnit
) {
163 if(deriveUnit
== null){
167 deriveUnit
= HibernateProxyHelper
.deproxy(deriveUnit
);
168 if(deriveUnit
.getCollection() != null){
169 deleteCandidates
.add(new CdmAuthority(deriveUnit
.getCollection(), UPDATE_DELETE
));
171 for(SpecimenOrObservationBase sob
: deriveUnit
.getOriginals()){
175 deleteCandidates
.add(new CdmAuthority(sob
, UPDATE_DELETE
));
176 if(sob
instanceof FieldUnit
){
177 addDeleteCandidates(deleteCandidates
, (FieldUnit
)sob
);
179 addDeleteCandidates(deleteCandidates
, (DerivedUnit
)sob
);
184 private void addDeleteCandidates(Set
<CdmAuthority
> deleteCandidates
, FieldUnit fieldUnit
) {
185 if(fieldUnit
== null){
188 fieldUnit
= HibernateProxyHelper
.deproxy(fieldUnit
);
189 if(fieldUnit
.getGatheringEvent() != null){
190 addDeleteCandidates(deleteCandidates
, fieldUnit
.getGatheringEvent().getActor());
195 * @param deleteCandidates
196 * @param nomenclaturalReference
198 private void addDeleteCandidates(Set
<CdmAuthority
> deleteCandidates
, Reference reference
) {
199 if(reference
== null){
202 reference
= HibernateProxyHelper
.deproxy(reference
);
203 deleteCandidates
.add(new CdmAuthority(reference
, UPDATE_DELETE
));
204 addDeleteCandidates(deleteCandidates
, reference
.getAuthorship());
205 addDeleteCandidates(deleteCandidates
, reference
.getInReference());
208 private void addDeleteCandidates(Set
<CdmAuthority
> deleteCandidates
, AgentBase
<?
> agent
) {
212 agent
= HibernateProxyHelper
.deproxy(agent
);
213 deleteCandidates
.add(new CdmAuthority(agent
, UPDATE_DELETE
));
214 if(agent
instanceof TeamOrPersonBase
){
215 if(agent
instanceof Team
){
216 List
<Person
> members
= ((Team
)agent
).getTeamMembers();
218 for(Person p
: members
){
220 deleteCandidates
.add(new CdmAuthority(p
, UPDATE_DELETE
));
233 * @param deleteCandidates
235 private void deleteAuthorities(EventSource session
, Set
<CdmAuthority
> deleteCandidates
) {
237 if(deleteCandidates
.isEmpty()){
241 Collection
<String
> authorityStrings
= new ArrayList
<String
>(deleteCandidates
.size());
242 deleteCandidates
.forEach( dc
-> authorityStrings
.add(dc
.toString()));
244 // -----------------------------------------------------------------------------------------
245 // this needs to be executed in a separate session to avoid concurrent modification problems
246 Session newSession
= session
.getSessionFactory().openSession();
247 Transaction txState
= newSession
.beginTransaction();
249 Query userQuery
= newSession
.createQuery("select u from User u join u.grantedAuthorities ga where ga.authority in (:authorities)");
250 userQuery
.setParameterList("authorities", authorityStrings
);
251 List
<User
> users
= userQuery
.list();
252 for(User user
: users
){
253 List
<GrantedAuthority
> deleteFromUser
= user
.getGrantedAuthorities().stream().filter(
254 ga
-> authorityStrings
.contains(ga
.getAuthority())
256 .collect(Collectors
.toList());
257 user
.getGrantedAuthorities().removeAll(deleteFromUser
);
260 Query groupQuery
= newSession
.createQuery("select g from Group g join g.grantedAuthorities ga where ga.authority in (:authorities)");
261 groupQuery
.setParameterList("authorities", authorityStrings
);
262 List
<Group
> groups
= groupQuery
.list();
263 for(Group group
: groups
){
264 List
<GrantedAuthority
> deleteFromUser
= group
.getGrantedAuthorities().stream().filter(
265 ga
-> authorityStrings
.contains(ga
.getAuthority())
267 .collect(Collectors
.toList());
268 group
.getGrantedAuthorities().removeAll(deleteFromUser
);
274 // -----------------------------------------------------------------------------------------
276 String hql
= "delete from GrantedAuthorityImpl as ga where ga.authority in (:authorities)";
277 Query deleteQuery
= session
.createQuery(hql
);
278 deleteQuery
.setParameterList("authorities", authorityStrings
);
279 deleteQuery
.setFlushMode(FlushMode
.MANUAL
); // workaround for HHH-11822 (https://hibernate.atlassian.net/browse/HHH-11822)
280 deleteQuery
.executeUpdate();
285 public boolean requiresPostCommitHanding(EntityPersister persister
) {