Project

General

Profile

Download (12.3 KB) Statistics
| Branch: | Tag: | Revision:
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
 * This Hibernate {@link PostUpdateEventListener} is responsible for
50
 * revoking GrantedAuthorities from any user which is having per entity
51
 * permissions in the object graph of the `Registration`being updated
52
 * This encompasses GrantedAuthotities with the CRUD values CRUD.UPDATE, CRUD.DELETE.
53
 * Please refer to the method documentation of {@link #collectDeleteCandidates(Registration)}
54
 * for further details.
55
 * <p>
56
 * The according permissions are revoked when the RegistrationStatus is being changed
57
 * by a database update. The RegistrationStatus causing this are contained in the constant
58
 * {@link GrantedAuthorityRevokingRegistrationUpdateLister#MODIFICATION_STOP_STATES MODIFICATION_STOP_STATES}
59
 *
60
 *
61
 * @author a.kohlbecker
62
 * @since Dec 18, 2017
63
 *
64
 */
65
public class GrantedAuthorityRevokingRegistrationUpdateLister implements PostUpdateEventListener {
66

    
67
    private static final long serialVersionUID = -3542204523291766866L;
68

    
69
    /**
70
     * Registrations having these states must no longer be modifiable by users having
71
     * only per entity permissions on the Registration subgraph.
72
     */
73
    private static final EnumSet<RegistrationStatus> MODIFICATION_STOP_STATES = EnumSet.of(
74
            RegistrationStatus.PUBLISHED,
75
            RegistrationStatus.READY,
76
            RegistrationStatus.REJECTED
77
            );
78

    
79
    private static final EnumSet<CRUD> UPDATE_DELETE = EnumSet.of(CRUD.UPDATE, CRUD.DELETE);
80

    
81
    private static final EnumSet<CRUD> UPDATE = EnumSet.of(CRUD.UPDATE);
82

    
83
    /**
84
     * {@inheritDoc}
85
     */
86
    @Override
87
    public void onPostUpdate(PostUpdateEvent event) {
88
        if( event.getEntity() instanceof Registration){
89
            Registration reg = (Registration)event.getEntity();
90
            if(reg.getStatus() != null && MODIFICATION_STOP_STATES.contains(reg.getStatus())){
91
                Set<CdmAuthority> deleteCandidates = collectDeleteCandidates(reg);
92
                deleteAuthorities(event.getSession(), deleteCandidates);
93
            }
94
        }
95
    }
96

    
97
    /**
98
     * Walks the entity graph of the Registration instance and collects all authorities which
99
     * could have been granted to users. Code parts in which this could have happened can be
100
     * found by searching for usage of the methods {@link eu.etaxonomy.cdm.vaadin.permission.UserHelper#createAuthorityForCurrentUser(eu.etaxonomy.cdm.model.common.CdmBase, EnumSet, String)
101
     * UserHelper.createAuthorityForCurrentUser(eu.etaxonomy.cdm.model.common.CdmBase, EnumSet, String)} and {@link eu.etaxonomy.cdm.vaadin.permission.UserHelper#createAuthorityForCurrentUser(Class, Integer, EnumSet, String)
102
     * UserHelper.createAuthorityForCurrentUser(Class, Integer, EnumSet, String)}
103
     * <p>
104
     * At the time of implementing this function these places are:
105
     * <ul>
106
     *  <li><code>RegistrationEditorPresenter.guaranteePerEntityCRUDPermissions(...)</code></li>
107
     *  <li><code>RegistrationWorkingsetPresenter.createNewRegistrationForName(Integer taxonNameId)</code></li>
108
     *  <li><code>TaxonNameEditorPresenter.guaranteePerEntityCRUDPermissions(...)</code></li>
109
     *  <li><code>ReferenceEditorPresenter.guaranteePerEntityCRUDPermissions(...)</code></li>
110
     *  <li><code>PersonField.commit()</code></li>
111
     *  <li><code>TeamOrPersonField.commit()</code></li>
112
     *  <li><code>SpecimenTypeDesignationWorkingsetEditorPresenter.saveBean(SpecimenTypeDesignationWorkingSetDTO dto)</code></li>
113
     * </ul>
114
     *
115
     * @param reg the Registration
116
     * @return the set of all possible CdmAuthorities that could have been granted to
117
     * individual users.
118
     */
119
    private Set<CdmAuthority> collectDeleteCandidates(Registration reg){
120
        Set<CdmAuthority> deleteCandidates = new HashSet<CdmAuthority>();
121
        // add authority for Registration
122
        deleteCandidates.add(new CdmAuthority(reg,  RegistrationStatus.PREPARATION.name(), UPDATE));
123
        if(reg.getName() != null){
124
            addDeleteCandidates(deleteCandidates, reg.getName());
125
        }
126
        for(TypeDesignationBase td : reg.getTypeDesignations()){
127
            addDeleteCandidates(deleteCandidates, td);
128
        }
129

    
130
        return deleteCandidates;
131

    
132
    }
133

    
134
    /**
135
     * @param deleteCandidates
136
     * @param name
137
     */
138
    private void addDeleteCandidates(Set<CdmAuthority> deleteCandidates, TaxonName name) {
139
        if(name == null){
140
            return;
141
        }
142
        name = HibernateProxyHelper.deproxy(name);
143
        deleteCandidates.add(new CdmAuthority(name, UPDATE_DELETE));
144
        addDeleteCandidates(deleteCandidates, name.getNomenclaturalReference());
145
        addDeleteCandidates(deleteCandidates, name.getCombinationAuthorship());
146
        addDeleteCandidates(deleteCandidates, name.getExCombinationAuthorship());
147
        addDeleteCandidates(deleteCandidates, name.getBasionymAuthorship());
148
        addDeleteCandidates(deleteCandidates, name.getExBasionymAuthorship());
149
    }
150

    
151

    
152
    /**
153
     * @param deleteCandidates
154
     * @param td
155
     */
156
    private void addDeleteCandidates(Set<CdmAuthority> deleteCandidates, TypeDesignationBase td) {
157
        if(td == null){
158
            return;
159
        }
160
        td = HibernateProxyHelper.deproxy(td);
161
        deleteCandidates.add(new CdmAuthority(td, UPDATE_DELETE));
162
        addDeleteCandidates(deleteCandidates, td.getCitation());
163
        if(td instanceof SpecimenTypeDesignation){
164
            SpecimenTypeDesignation std = (SpecimenTypeDesignation)td;
165
            addDeleteCandidates(deleteCandidates, std.getTypeSpecimen());
166
        }
167
    }
168

    
169
    /**
170
     * @param deleteCandidates
171
     * @param typeSpecimen
172
     */
173
    private void addDeleteCandidates(Set<CdmAuthority> deleteCandidates, DerivedUnit deriveUnit) {
174
        if(deriveUnit == null){
175
            return;
176
        }
177

    
178
        deriveUnit = HibernateProxyHelper.deproxy(deriveUnit);
179
        if(deriveUnit.getCollection() != null){
180
            deleteCandidates.add(new CdmAuthority(deriveUnit.getCollection(), UPDATE_DELETE));
181
        }
182
        for(SpecimenOrObservationBase sob : deriveUnit.getOriginals()){
183
            if(sob == null){
184
                continue;
185
            }
186
            deleteCandidates.add(new CdmAuthority(sob, UPDATE_DELETE));
187
            if(sob instanceof FieldUnit){
188
                addDeleteCandidates(deleteCandidates, (FieldUnit)sob);
189
            } else {
190
                addDeleteCandidates(deleteCandidates, (DerivedUnit)sob);
191
            }
192
        }
193
    }
194

    
195
    private void addDeleteCandidates(Set<CdmAuthority> deleteCandidates, FieldUnit fieldUnit) {
196
        if(fieldUnit == null){
197
            return;
198
        }
199
        fieldUnit = HibernateProxyHelper.deproxy(fieldUnit);
200
        if(fieldUnit.getGatheringEvent() != null){
201
            addDeleteCandidates(deleteCandidates, fieldUnit.getGatheringEvent().getActor());
202
        }
203
    }
204

    
205
    /**
206
     * @param deleteCandidates
207
     * @param nomenclaturalReference
208
     */
209
    private void addDeleteCandidates(Set<CdmAuthority> deleteCandidates, Reference reference) {
210
        if(reference == null){
211
            return;
212
        }
213
        reference = HibernateProxyHelper.deproxy(reference);
214
        deleteCandidates.add(new CdmAuthority(reference, UPDATE_DELETE));
215
        addDeleteCandidates(deleteCandidates, reference.getAuthorship());
216
        addDeleteCandidates(deleteCandidates, reference.getInReference());
217
    }
218

    
219
    private void addDeleteCandidates(Set<CdmAuthority> deleteCandidates, AgentBase<?> agent) {
220
        if(agent == null){
221
            return;
222
        }
223
        agent = HibernateProxyHelper.deproxy(agent);
224
        deleteCandidates.add(new CdmAuthority(agent, UPDATE_DELETE));
225
        if(agent instanceof TeamOrPersonBase){
226
            if(agent instanceof Team){
227
                List<Person> members = ((Team)agent).getTeamMembers();
228
                if(members != null){
229
                    for(Person p : members){
230
                        if(p != null){
231
                            deleteCandidates.add(new CdmAuthority(p, UPDATE_DELETE));
232
                        }
233
                    }
234
                }
235
            }
236
        }
237

    
238
    }
239

    
240

    
241

    
242

    
243
    /**
244
     * @param deleteCandidates
245
     */
246
    private void deleteAuthorities(EventSource session, Set<CdmAuthority> deleteCandidates) {
247

    
248
        if(deleteCandidates.isEmpty()){
249
            return;
250
        }
251

    
252
        Collection<String> authorityStrings = new ArrayList<String>(deleteCandidates.size());
253
        deleteCandidates.forEach( dc -> authorityStrings.add(dc.toString()));
254

    
255
        // -----------------------------------------------------------------------------------------
256
        // this needs to be executed in a separate session to avoid concurrent modification problems
257
        Session newSession = session.getSessionFactory().openSession();
258
        Transaction txState = newSession.beginTransaction();
259

    
260
        Query userQuery = newSession.createQuery("select u from User u join u.grantedAuthorities ga where ga.authority in (:authorities)");
261
        userQuery.setParameterList("authorities", authorityStrings);
262
        List<User> users = userQuery.list();
263
        for(User user : users){
264
            List<GrantedAuthority> deleteFromUser = user.getGrantedAuthorities().stream().filter(
265
                        ga -> authorityStrings.contains(ga.getAuthority())
266
                    )
267
                    .collect(Collectors.toList());
268
            user.getGrantedAuthorities().removeAll(deleteFromUser);
269
        }
270

    
271
        Query groupQuery = newSession.createQuery("select g from Group g join g.grantedAuthorities ga where ga.authority in (:authorities)");
272
        groupQuery.setParameterList("authorities", authorityStrings);
273
        List<Group> groups = groupQuery.list();
274
        for(Group group : groups){
275
            List<GrantedAuthority> deleteFromUser = group.getGrantedAuthorities().stream().filter(
276
                        ga -> authorityStrings.contains(ga.getAuthority())
277
                    )
278
                    .collect(Collectors.toList());
279
            group.getGrantedAuthorities().removeAll(deleteFromUser);
280
        }
281

    
282
        newSession.flush();
283
        txState.commit();
284
        newSession.close();
285
        // -----------------------------------------------------------------------------------------
286

    
287
        String hql = "delete from GrantedAuthorityImpl as ga where ga.authority in (:authorities)";
288
        Query deleteQuery = session.createQuery(hql);
289
        deleteQuery.setParameterList("authorities", authorityStrings);
290
        deleteQuery.setFlushMode(FlushMode.MANUAL); // workaround for  HHH-11822 (https://hibernate.atlassian.net/browse/HHH-11822)
291
        deleteQuery.executeUpdate();
292

    
293
    }
294

    
295
    @Override
296
    public boolean requiresPostCommitHanding(EntityPersister persister) {
297
        return false;
298
    }
299

    
300
}
    (1-1/1)