typo
[cdm-vaadin.git] / src / main / java / eu / etaxonomy / cdm / persistence / hibernate / GrantedAuthorityRevokingRegistrationUpdateLister.java
1 /**
2 * Copyright (C) 2017 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
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.
8 */
9 package eu.etaxonomy.cdm.persistence.hibernate;
10
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;
16 import java.util.Set;
17 import java.util.stream.Collectors;
18
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;
28
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;
47
48 /**
49 * @author a.kohlbecker
50 * @since Dec 18, 2017
51 *
52 */
53 public class GrantedAuthorityRevokingRegistrationUpdateLister implements PostUpdateEventListener {
54
55 private static final long serialVersionUID = -3542204523291766866L;
56
57 /**
58 *
59 * Registrations having these states must no longer be modifiable by users having only per entity permissions on the
60 * Registration subgraph
61 */
62 private static final EnumSet<RegistrationStatus> MODIFICATION_STOP_STATES = EnumSet.of(
63 RegistrationStatus.PUBLISHED,
64 RegistrationStatus.READY,
65 RegistrationStatus.REJECTED
66 );
67
68 private static final EnumSet<CRUD> UPDATE_DELETE = EnumSet.of(CRUD.UPDATE, CRUD.DELETE);
69
70 private static final EnumSet<CRUD> UPDATE = EnumSet.of(CRUD.UPDATE);
71
72 /**
73 * {@inheritDoc}
74 */
75 @Override
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);
82 }
83 }
84 }
85
86 /**
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)}
92 * <p>
93 * At the time of implementing this function these places are:
94 * <ul>
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>
102 * </ul>
103 *
104 * @param reg the Registration
105 * @return the set of all possible CdmAuthorities that could have been granted to
106 * individual users.
107 */
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());
114 }
115 for(TypeDesignationBase td : reg.getTypeDesignations()){
116 addDeleteCandidates(deleteCandidates, td);
117 }
118
119 return deleteCandidates;
120
121 }
122
123 /**
124 * @param deleteCandidates
125 * @param name
126 */
127 private void addDeleteCandidates(Set<CdmAuthority> deleteCandidates, TaxonName name) {
128 if(name == null){
129 return;
130 }
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());
138 }
139
140
141 /**
142 * @param deleteCandidates
143 * @param td
144 */
145 private void addDeleteCandidates(Set<CdmAuthority> deleteCandidates, TypeDesignationBase td) {
146 if(td == null){
147 return;
148 }
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());
155 }
156 }
157
158 /**
159 * @param deleteCandidates
160 * @param typeSpecimen
161 */
162 private void addDeleteCandidates(Set<CdmAuthority> deleteCandidates, DerivedUnit deriveUnit) {
163 if(deriveUnit == null){
164 return;
165 }
166
167 deriveUnit = HibernateProxyHelper.deproxy(deriveUnit);
168 if(deriveUnit.getCollection() != null){
169 deleteCandidates.add(new CdmAuthority(deriveUnit.getCollection(), UPDATE_DELETE));
170 }
171 for(SpecimenOrObservationBase sob : deriveUnit.getOriginals()){
172 if(sob == null){
173 continue;
174 }
175 deleteCandidates.add(new CdmAuthority(sob, UPDATE_DELETE));
176 if(sob instanceof FieldUnit){
177 addDeleteCandidates(deleteCandidates, (FieldUnit)sob);
178 } else {
179 addDeleteCandidates(deleteCandidates, (DerivedUnit)sob);
180 }
181 }
182 }
183
184 private void addDeleteCandidates(Set<CdmAuthority> deleteCandidates, FieldUnit fieldUnit) {
185 if(fieldUnit == null){
186 return;
187 }
188 fieldUnit = HibernateProxyHelper.deproxy(fieldUnit);
189 if(fieldUnit.getGatheringEvent() != null){
190 addDeleteCandidates(deleteCandidates, fieldUnit.getGatheringEvent().getActor());
191 }
192 }
193
194 /**
195 * @param deleteCandidates
196 * @param nomenclaturalReference
197 */
198 private void addDeleteCandidates(Set<CdmAuthority> deleteCandidates, Reference reference) {
199 if(reference == null){
200 return;
201 }
202 reference = HibernateProxyHelper.deproxy(reference);
203 deleteCandidates.add(new CdmAuthority(reference, UPDATE_DELETE));
204 addDeleteCandidates(deleteCandidates, reference.getAuthorship());
205 addDeleteCandidates(deleteCandidates, reference.getInReference());
206 }
207
208 private void addDeleteCandidates(Set<CdmAuthority> deleteCandidates, AgentBase<?> agent) {
209 if(agent == null){
210 return;
211 }
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();
217 if(members != null){
218 for(Person p : members){
219 if(p != null){
220 deleteCandidates.add(new CdmAuthority(p, UPDATE_DELETE));
221 }
222 }
223 }
224 }
225 }
226
227 }
228
229
230
231
232 /**
233 * @param deleteCandidates
234 */
235 private void deleteAuthorities(EventSource session, Set<CdmAuthority> deleteCandidates) {
236
237 if(deleteCandidates.isEmpty()){
238 return;
239 }
240
241 Collection<String> authorityStrings = new ArrayList<String>(deleteCandidates.size());
242 deleteCandidates.forEach( dc -> authorityStrings.add(dc.toString()));
243
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();
248
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())
255 )
256 .collect(Collectors.toList());
257 user.getGrantedAuthorities().removeAll(deleteFromUser);
258 }
259
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())
266 )
267 .collect(Collectors.toList());
268 group.getGrantedAuthorities().removeAll(deleteFromUser);
269 }
270
271 newSession.flush();
272 txState.commit();
273 newSession.close();
274 // -----------------------------------------------------------------------------------------
275
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();
281
282 }
283
284 @Override
285 public boolean requiresPostCommitHanding(EntityPersister persister) {
286 return false;
287 }
288
289 }