Project

General

Profile

Download (11.6 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
 * @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 midifiable 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, (Reference)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
}
    (1-1/1)