Project

General

Profile

Download (16.8 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.api.util;
10

    
11
import java.io.Serializable;
12
import java.util.Collection;
13
import java.util.EnumSet;
14
import java.util.HashSet;
15
import java.util.Set;
16
import java.util.UUID;
17

    
18
import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;
19
import org.springframework.beans.factory.annotation.Autowired;
20
import org.springframework.beans.factory.annotation.Qualifier;
21
import org.springframework.context.annotation.Lazy;
22
import org.springframework.security.authentication.AnonymousAuthenticationToken;
23
import org.springframework.security.authentication.AuthenticationProvider;
24
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
25
import org.springframework.security.core.Authentication;
26
import org.springframework.security.core.GrantedAuthority;
27
import org.springframework.security.core.context.SecurityContext;
28
import org.springframework.security.core.context.SecurityContextHolder;
29
import org.springframework.security.core.userdetails.UserDetails;
30
import org.springframework.transaction.TransactionStatus;
31

    
32
import eu.etaxonomy.cdm.api.application.CdmRepository;
33
import eu.etaxonomy.cdm.api.application.RunAsAuthenticator;
34
import eu.etaxonomy.cdm.database.PermissionDeniedException;
35
import eu.etaxonomy.cdm.model.ICdmEntityUuidCacher;
36
import eu.etaxonomy.cdm.model.common.CdmBase;
37
import eu.etaxonomy.cdm.model.permission.CRUD;
38
import eu.etaxonomy.cdm.model.permission.GrantedAuthorityImpl;
39
import eu.etaxonomy.cdm.model.permission.PermissionClass;
40
import eu.etaxonomy.cdm.model.permission.User;
41
import eu.etaxonomy.cdm.persistence.permission.CdmAuthority;
42
import eu.etaxonomy.cdm.persistence.permission.CdmAuthorityParsingException;
43
import eu.etaxonomy.cdm.persistence.permission.ICdmPermissionEvaluator;
44
import eu.etaxonomy.cdm.persistence.permission.Role;
45

    
46
/**
47
 * @author a.kohlbecker
48
 * @since May 19, 2017
49
 */
50
public class CdmUserHelper implements UserHelper, Serializable {
51

    
52
    private static final long serialVersionUID = -2521474709047255979L;
53

    
54
    public static final Logger logger = LogManager.getLogger(CdmUserHelper.class);
55

    
56
    @Autowired
57
    private ICdmPermissionEvaluator permissionEvaluator;
58

    
59
    @Autowired
60
    @Lazy
61
    @Qualifier("cdmRepository")
62
    protected CdmRepository repo;
63

    
64
    private AuthenticationProvider runAsAuthenticationProvider;
65

    
66
    @Autowired(required=false)
67
    @Qualifier("runAsAuthenticationProvider")
68
    public void setRunAsAuthenticationProvider(AuthenticationProvider runAsAuthenticationProvider){
69
        this.runAsAuthenticationProvider = runAsAuthenticationProvider;
70
        runAsAutheticator.setRunAsAuthenticationProvider(runAsAuthenticationProvider);
71
    }
72

    
73
    RunAsAuthenticator runAsAutheticator = new RunAsAuthenticator();
74

    
75
    private SecurityContextAccess securityContextAccess;
76

    
77
    public CdmUserHelper(){
78
        super();
79
    }
80

    
81
    protected ICdmPermissionEvaluator permissionEvaluator() {
82
        return permissionEvaluator;
83
    }
84

    
85
    protected CdmRepository repo() {
86
        return repo;
87
    }
88

    
89
    @Override
90
    public boolean userIsAutheticated() {
91
        Authentication authentication = getAuthentication();
92
        if(authentication != null && !AnonymousAuthenticationToken.class.equals(authentication.getClass())){
93
            return authentication.isAuthenticated();
94
        }
95
        return false;
96
    }
97

    
98
    @Override
99
    public boolean userIsAnnonymous() {
100
        Authentication authentication = getAuthentication();
101
        return authentication != null
102
                && authentication.isAuthenticated()
103
                && authentication instanceof AnonymousAuthenticationToken;
104
    }
105

    
106
    @Override
107
    public User user() {
108
        Authentication authentication = getAuthentication();
109
        if(authentication != null && authentication.getPrincipal() != null && authentication.getPrincipal() instanceof User) {
110
            return (User) authentication.getPrincipal();
111
        }
112
        return null;
113
    }
114

    
115
    @Override
116
    public String userName() {
117
        Authentication authentication = getAuthentication();
118
        if(authentication != null) {
119
            return authentication.getName();
120
        }
121
        return null;
122
    }
123

    
124
    @Override
125
    public boolean userIsAdmin() {
126
        Authentication authentication = getAuthentication();
127
        if(authentication != null) {
128
            return authentication.getAuthorities().stream().anyMatch(a -> {
129
                return a.getAuthority().equals(Role.ROLE_ADMIN.getAuthority());
130
            });
131
        }
132
        return false;
133
    }
134

    
135
    @Override
136
    public boolean userIs(IRoleProber iRoleProbe) {
137
        return iRoleProbe.checkForRole(getAuthentication());
138
    }
139

    
140
    @Override
141
    public boolean userHasPermission(CdmBase entity, Object ... args){
142
        EnumSet<CRUD> crudSet = crudSetFromArgs(args);
143
        try {
144
            return permissionEvaluator().hasPermission(getAuthentication(), entity, crudSet);
145
        } catch (PermissionDeniedException e){
146
            //IGNORE
147
        }
148
        return false;
149
    }
150

    
151
    /**
152
     * @deprecated not performance optimized by using the cache,
153
     * use {@link #userHasPermission(Class, UUID, Object...)} instead
154
     */
155
    @Override
156
    @Deprecated
157
    public boolean userHasPermission(Class<? extends CdmBase> cdmType, Integer entitiyId, Object ... args){
158
        EnumSet<CRUD> crudSet = crudSetFromArgs(args);
159
        try {
160
            CdmBase entity = repo().getCommonService().find(cdmType, entitiyId);
161
            return permissionEvaluator().hasPermission(getAuthentication(), entity, crudSet);
162
        } catch (PermissionDeniedException e){
163
            //IGNORE
164
        }
165
        return false;
166
    }
167

    
168
    @Override
169
    public boolean userHasPermission(Class<? extends CdmBase> cdmType, UUID entitiyUuid, Object ... args){
170
        EnumSet<CRUD> crudSet = crudSetFromArgs(args);
171
        try {
172
            CdmBase entity = entity(cdmType, entitiyUuid);
173
            return permissionEvaluator().hasPermission(getAuthentication(), entity, crudSet);
174
        } catch (PermissionDeniedException e){
175
            //IGNORE
176
        }
177
        return false;
178
    }
179

    
180
    protected CdmBase entity(Class<? extends CdmBase> cdmType, UUID entitiyUuid) {
181
        CdmBase entity = entityFromCache(cdmType, entitiyUuid);
182
        if(entity == null){
183
            entity = repo().getCommonService().find(cdmType, entitiyUuid);
184
            if(getCache() != null && entity != null){
185
                getCache().putToCache(entity);
186
            }
187
        }
188
        return entity;
189
    }
190

    
191
    @Override
192
    public boolean userHasPermission(Class<? extends CdmBase> cdmType, Object ... args){
193
        EnumSet<CRUD> crudSet = crudSetFromArgs(args);
194
        try {
195
            return permissionEvaluator().hasPermission(getAuthentication(), cdmType, crudSet);
196
        } catch (PermissionDeniedException e){
197
            //IGNORE
198
        }
199
        return false;
200
    }
201

    
202
    @Override
203
    public void logout() {
204
        SecurityContext context = SecurityContextHolder.getContext();
205
        context.setAuthentication(null);
206
        SecurityContextHolder.clearContext();
207
    }
208

    
209
    private EnumSet<CRUD> crudSetFromArgs(Object[] args) {
210
        EnumSet<CRUD> crudSet = EnumSet.noneOf(CRUD.class);
211
        for(int i = 0; i < args.length; i++){
212
            try {
213
                crudSet.add(CRUD.valueOf(args[i].toString()));
214
            } catch (Exception e){
215
                throw new IllegalArgumentException("could not add " + args[i], e);
216
            }
217
        }
218
        return crudSet;
219
    }
220

    
221
    private SecurityContext currentSecurityContext() {
222
        if(securityContextAccess != null){
223
            return securityContextAccess.currentSecurityContext();
224
        }
225
        return SecurityContextHolder.getContext();
226
    }
227

    
228
    @Override
229
    public Authentication getAuthentication() {
230
        return currentSecurityContext().getAuthentication();
231
    }
232

    
233
    @Override
234
    public CdmAuthority createAuthorityFor(String username, CdmBase cdmEntity, EnumSet<CRUD> crud, String property) {
235

    
236
        TransactionStatus txStatus = repo().startTransaction();
237
        UserDetails userDetails = repo().getUserService().loadUserByUsername(username);
238
        boolean newAuthorityAdded = false;
239
        CdmAuthority authority = null;
240
        User user = (User)userDetails;
241
        if(userDetails != null){
242
            try{
243
                // flush all pending transactions before changing the authentication,
244
                // see https://dev.e-taxonomy.eu/redmine/issues/8066 for discussion
245
                // in case of problems we may want to use another transaction propagation level
246
                // instead (PROPAGATION_REQUIRES_NEW) which would require to reload the cdm entity.
247
                repo().getSession().flush();
248
                getRunAsAutheticator().runAsAuthentication(Role.ROLE_USER_MANAGER);
249
                authority = new CdmAuthority(cdmEntity, property, crud);
250
                try {
251
                    GrantedAuthorityImpl grantedAuthority = repo().getGrantedAuthorityService().findAuthorityString(authority.toString());
252
                    if(grantedAuthority == null){
253
                        grantedAuthority = authority.asNewGrantedAuthority();
254
                    }
255
                    newAuthorityAdded = user.getGrantedAuthorities().add(grantedAuthority);
256
                } catch (CdmAuthorityParsingException e) {
257
                    getRunAsAutheticator().restoreAuthentication();
258
                    throw new RuntimeException(e);
259
                }
260
                repo().getSession().flush();
261
            } finally {
262
                // in any case restore the previous authentication
263
                getRunAsAutheticator().restoreAuthentication();
264
            }
265
            logger.debug("new authority for " + username + ": " + authority.toString());
266
            Authentication authentication = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
267
            SecurityContextHolder.getContext().setAuthentication(authentication);
268
            logger.debug("security context refreshed with user " + username);
269
        }
270
        repo().commitTransaction(txStatus);
271
        return newAuthorityAdded ? authority : null;
272

    
273
    }
274

    
275
    /**
276
     * @param username
277
     * @param cdmType
278
     * @param entitiyId
279
     * @param crud
280
     * @return
281
     * @deprecated not performance optimized by using the cache,
282
     * use {@link #createAuthorityFor(String, Class, UUID, EnumSet, String)} instead
283
     */
284
    @Override
285
    @Deprecated
286
    public CdmAuthority createAuthorityFor(String username, Class<? extends CdmBase> cdmType, Integer entitiyId, EnumSet<CRUD> crud, String property) {
287

    
288
        CdmBase cdmEntity = repo().getCommonService().find(cdmType, entitiyId);
289
        return createAuthorityFor(username, cdmEntity, crud, property);
290
    }
291

    
292
    @Override
293
    public CdmAuthority createAuthorityFor(String username, Class<? extends CdmBase> cdmType, UUID entitiyUuid, EnumSet<CRUD> crud, String property) {
294

    
295
        CdmBase cdmEntity = entity(cdmType, entitiyUuid);
296
        return createAuthorityFor(username, cdmEntity, crud, property);
297
    }
298

    
299
    @Override
300
    public CdmAuthority createAuthorityForCurrentUser(CdmBase cdmEntity, EnumSet<CRUD> crud, String property) {
301
        return createAuthorityFor(userName(), cdmEntity, crud, property);
302

    
303
    }
304

    
305
    @Override
306
    public CdmAuthority createAuthorityForCurrentUser(Class<? extends CdmBase> cdmType, Integer entitiyId, EnumSet<CRUD> crud, String property) {
307
        return createAuthorityFor(userName(), cdmType, entitiyId, crud, property);
308
    }
309

    
310
    @Override
311
    public CdmAuthority createAuthorityForCurrentUser(Class<? extends CdmBase> cdmType, UUID entitiyUuid, EnumSet<CRUD> crud, String property) {
312
        return createAuthorityFor(userName(), cdmType, entitiyUuid, crud, property);
313
    }
314

    
315
    @Override
316
    public void removeAuthorityForCurrentUser(CdmAuthority cdmAuthority) {
317
        removeAuthorityForCurrentUser(userName(), cdmAuthority);
318

    
319
    }
320

    
321
    @Override
322
    public void removeAuthorityForCurrentUser(String username, CdmAuthority cdmAuthority) {
323

    
324
        TransactionStatus txStatus = repo().startTransaction();
325
        UserDetails userDetails = repo().getUserService().loadUserByUsername(username);
326
        User user = (User)userDetails;
327
        if(userDetails != null){
328
            try {
329
                getRunAsAutheticator().runAsAuthentication(Role.ROLE_USER_MANAGER);
330
                user.getGrantedAuthorities().remove(cdmAuthority);
331
                repo().getSession().flush();
332
                logger.debug("security context refreshed with user " + username);
333
            } finally {
334
                getRunAsAutheticator().restoreAuthentication();
335
            }
336
            logger.debug("authority removed from " + username + ": " + cdmAuthority.toString());
337
            Authentication authentication = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
338
            SecurityContextHolder.getContext().setAuthentication(authentication);
339
            logger.debug("security context refreshed with user " + username);
340
        }
341
        repo().commitTransaction(txStatus);
342
    }
343

    
344
    @Override
345
    public Collection<CdmAuthority> findUserPermissions(CdmBase cdmEntity, EnumSet<CRUD> crud) {
346
        Set<CdmAuthority> matches = new HashSet<>();
347
        PermissionClass permissionClass = PermissionClass.getValueOf(cdmEntity);
348
        Collection<? extends GrantedAuthority> authorities = getAuthentication().getAuthorities();
349
        for(GrantedAuthority ga : authorities){
350
            try {
351
                CdmAuthority cdmAuthority = CdmAuthority.fromGrantedAuthority(ga);
352
                if(cdmAuthority.getPermissionClass().equals(permissionClass)){
353
                    if(cdmAuthority.getOperation().containsAll(crud)){
354
                        if(cdmAuthority.hasTargetUuid() && cdmAuthority.getTargetUUID().equals(cdmEntity.getUuid())){
355
                            matches.add(cdmAuthority);
356
                        } else {
357
                            matches.add(cdmAuthority);
358
                        }
359
                    }
360
                }
361
            } catch (CdmAuthorityParsingException e) {
362
                continue;
363
            }
364
        }
365
        return matches;
366
    }
367

    
368
    @Override
369
    public <T extends CdmBase> Collection<CdmAuthority> findUserPermissions(Class<T> cdmType, EnumSet<CRUD> crud) {
370
        Set<CdmAuthority> matches = new HashSet<>();
371
        PermissionClass permissionClass = PermissionClass.getValueOf(cdmType);
372
        Collection<? extends GrantedAuthority> authorities = getAuthentication().getAuthorities();
373
        for(GrantedAuthority ga : authorities){
374
            try {
375
                CdmAuthority cdmAuthority = CdmAuthority.fromGrantedAuthority(ga);
376
                if(cdmAuthority.getPermissionClass().equals(permissionClass)){
377
                    if(cdmAuthority.getOperation().containsAll(crud)){
378
                        matches.add(cdmAuthority);
379
                    }
380
                }
381
            } catch (CdmAuthorityParsingException e) {
382
                continue;
383
            }
384
        }
385
        return matches;
386
    }
387

    
388
    @Override
389
    public void setSecurityContextAccess(SecurityContextAccess securityContextAccess) {
390
        this.securityContextAccess = securityContextAccess;
391
    }
392

    
393
    public RunAsAuthenticator getRunAsAutheticator() {
394
        if(runAsAutheticator == null){
395
          throw new RuntimeException("RunAsAuthenticator is missing! The application needs to be configured with security context.");
396
        }
397
        return runAsAutheticator;
398
    }
399

    
400
    public ICdmEntityUuidCacher getCache() {
401
        return null;
402
    }
403

    
404
    private CdmBase entityFromCache(Class<? extends CdmBase> cdmType, UUID entitiyUuid) {
405
        CdmBase entity = null;
406
        if(getCache() != null){
407
           entity = getCache().getFromCache(entitiyUuid);
408
           if(entity != null && !cdmType.isAssignableFrom(entity.getClass())){
409
               logger.error("Entity with " +  entitiyUuid + " does not match the required type");
410
               entity = null;
411
           }
412
        }
413
        return entity;
414
    }
415

    
416
    @Override
417
    public CdmUserHelper withCache(ICdmEntityUuidCacher cache){
418
        return new CachingCdmUserHelper(cache);
419
    }
420

    
421
    class CachingCdmUserHelper extends CdmUserHelper{
422

    
423
        private static final long serialVersionUID = -5010082174809972831L;
424

    
425
        private ICdmEntityUuidCacher cache;
426

    
427
        public CachingCdmUserHelper(ICdmEntityUuidCacher cache){
428
            this.cache = cache;
429
        }
430

    
431
        @Override
432
        public ICdmEntityUuidCacher getCache() {
433
            return cache;
434
        }
435

    
436
        @Override
437
        protected CdmRepository repo() {
438
            return CdmUserHelper.this.repo;
439
        }
440

    
441
        @Override
442
        protected ICdmPermissionEvaluator permissionEvaluator() {
443
            return CdmUserHelper.this.permissionEvaluator;
444
        }
445
    }
446

    
447
}
(2-2/15)