Project

General

Profile

Download (17 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.utility;
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.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.common.GrantedAuthorityImpl;
38
import eu.etaxonomy.cdm.model.common.User;
39
import eu.etaxonomy.cdm.persistence.hibernate.permission.CRUD;
40
import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmAuthority;
41
import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmAuthorityParsingException;
42
import eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermissionClass;
43
import eu.etaxonomy.cdm.persistence.hibernate.permission.ICdmPermissionEvaluator;
44
import eu.etaxonomy.cdm.persistence.hibernate.permission.Role;
45

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

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

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

    
57
    @Autowired
58
    private ICdmPermissionEvaluator permissionEvaluator;
59

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

    
65
    AuthenticationProvider runAsAuthenticationProvider;
66

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

    
74
    RunAsAuthenticator runAsAutheticator = new RunAsAuthenticator();
75

    
76
    private SecurityContextAccess securityContextAccess;
77

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

    
82
    /**
83
     * @return
84
     */
85
    protected ICdmPermissionEvaluator permissionEvaluator() {
86
        return permissionEvaluator;
87
    }
88

    
89
    /**
90
     * @return
91
     */
92
    protected CdmRepository repo() {
93
        return repo;
94
    }
95

    
96
    @Override
97
    public boolean userIsAutheticated() {
98
        Authentication authentication = getAuthentication();
99
        if(authentication != null && !AnonymousAuthenticationToken.class.equals(authentication.getClass())){
100
            return authentication.isAuthenticated();
101
        }
102
        return false;
103
    }
104

    
105

    
106
    @Override
107
    public boolean userIsAnnonymous() {
108
        Authentication authentication = getAuthentication();
109
        return authentication != null
110
                && authentication.isAuthenticated()
111
                && authentication instanceof AnonymousAuthenticationToken;
112
    }
113

    
114
    @Override
115
    public User user() {
116
        Authentication authentication = getAuthentication();
117
        if(authentication != null && authentication.getPrincipal() != null) {
118
            return (User) authentication.getPrincipal();
119
        }
120
        return null;
121
    }
122

    
123
    @Override
124
    public String userName() {
125
        Authentication authentication = getAuthentication();
126
        if(authentication != null) {
127
            return authentication.getName();
128
        }
129
        return null;
130
    }
131

    
132
    @Override
133
    public boolean userIsAdmin() {
134
        Authentication authentication = getAuthentication();
135
        if(authentication != null) {
136
            return authentication.getAuthorities().stream().anyMatch(a -> {
137
                return a.getAuthority().equals(Role.ROLE_ADMIN.getAuthority());
138
            });
139
        }
140
        return false;
141
    }
142

    
143
    /**
144
     * {@inheritDoc}
145
     */
146
    @Override
147
    public boolean userIs(RoleProbe roleProbe) {
148
        return roleProbe.checkForRole(getAuthentication());
149
    }
150

    
151
    @Override
152
    public boolean userHasPermission(CdmBase entity, Object ... args){
153
        EnumSet<CRUD> crudSet = crudSetFromArgs(args);
154
        try {
155
            return permissionEvaluator().hasPermission(getAuthentication(), entity, crudSet);
156
        } catch (PermissionDeniedException e){
157
            //IGNORE
158
        }
159
        return false;
160
    }
161

    
162
    /**
163
     * @deprecated not performance optimized by using the cache,
164
     * use {@link #userHasPermission(Class, UUID, Object...)} instead
165
     */
166
    @Override
167
    @Deprecated
168
    public boolean userHasPermission(Class<? extends CdmBase> cdmType, Integer entitiyId, Object ... args){
169
        EnumSet<CRUD> crudSet = crudSetFromArgs(args);
170
        try {
171
            CdmBase entity = repo().getCommonService().find(cdmType, entitiyId);
172
            return permissionEvaluator().hasPermission(getAuthentication(), entity, crudSet);
173
        } catch (PermissionDeniedException e){
174
            //IGNORE
175
        }
176
        return false;
177
    }
178

    
179
    @Override
180
    public boolean userHasPermission(Class<? extends CdmBase> cdmType, UUID entitiyUuid, Object ... args){
181
        EnumSet<CRUD> crudSet = crudSetFromArgs(args);
182
        try {
183
            CdmBase entity = entity(cdmType, entitiyUuid);
184
            return permissionEvaluator().hasPermission(getAuthentication(), entity, crudSet);
185
        } catch (PermissionDeniedException e){
186
            //IGNORE
187
        }
188
        return false;
189
    }
190

    
191
    /**
192
     * @param cdmType
193
     * @param entitiyUuid
194
     * @return
195
     */
196
    protected CdmBase entity(Class<? extends CdmBase> cdmType, UUID entitiyUuid) {
197
        CdmBase entity = entityFromCache(cdmType, entitiyUuid);
198
        if(entity == null){
199
            entity = repo().getCommonService().find(cdmType, entitiyUuid);
200
            if(getCache() != null && entity != null){
201
                getCache().put(entity);
202
            }
203
        }
204
        return entity;
205
    }
206

    
207
    @Override
208
    public boolean userHasPermission(Class<? extends CdmBase> cdmType, Object ... args){
209
        EnumSet<CRUD> crudSet = crudSetFromArgs(args);
210
        try {
211
            return permissionEvaluator().hasPermission(getAuthentication(), cdmType, crudSet);
212
        } catch (PermissionDeniedException e){
213
            //IGNORE
214
        }
215
        return false;
216
    }
217

    
218
    @Override
219
    public void logout() {
220
        SecurityContext context = SecurityContextHolder.getContext();
221
        context.setAuthentication(null);
222
        SecurityContextHolder.clearContext();
223
    }
224

    
225

    
226
    private EnumSet<CRUD> crudSetFromArgs(Object[] args) {
227
        EnumSet<CRUD> crudSet = EnumSet.noneOf(CRUD.class);
228
        for(int i = 0; i < args.length; i++){
229
            try {
230
                crudSet.add(CRUD.valueOf(args[i].toString()));
231
            } catch (Exception e){
232
                throw new IllegalArgumentException("could not add " + args[i], e);
233
            }
234
        }
235
        return crudSet;
236
    }
237

    
238

    
239
    private SecurityContext currentSecurityContext() {
240
        if(securityContextAccess != null){
241
            return securityContextAccess.currentSecurityContext();
242
        }
243
        return SecurityContextHolder.getContext();
244
    }
245

    
246
    /**
247
     * @return
248
     */
249
    @Override
250
    public Authentication getAuthentication() {
251
        return currentSecurityContext().getAuthentication();
252
    }
253

    
254
    /**
255
     * {@inheritDoc}
256
     *
257
     */
258
    @Override
259
    public CdmAuthority createAuthorityFor(String username, CdmBase cdmEntity, EnumSet<CRUD> crud, String property) {
260

    
261
        TransactionStatus txStatus = repo().startTransaction();
262
        UserDetails userDetails = repo().getUserService().loadUserByUsername(username);
263
        boolean newAuthorityAdded = false;
264
        CdmAuthority authority = null;
265
        User user = (User)userDetails;
266
        if(userDetails != null){
267
            try{
268
                getRunAsAutheticator().runAsAuthentication(Role.ROLE_USER_MANAGER);
269
                authority = new CdmAuthority(cdmEntity, property, crud);
270
                try {
271
                    GrantedAuthorityImpl grantedAuthority = repo().getGrantedAuthorityService().findAuthorityString(authority.toString());
272
                    if(grantedAuthority == null){
273
                        grantedAuthority = authority.asNewGrantedAuthority();
274
                    }
275
                    newAuthorityAdded = user.getGrantedAuthorities().add(grantedAuthority);
276
                } catch (CdmAuthorityParsingException e) {
277
                    throw new RuntimeException(e);
278
                }
279
                repo().getSession().flush();
280
            } finally {
281
                // in any case restore the previous authentication
282
                getRunAsAutheticator().restoreAuthentication();
283
            }
284
            logger.debug("new authority for " + username + ": " + authority.toString());
285
            Authentication authentication = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
286
            SecurityContextHolder.getContext().setAuthentication(authentication);
287
            logger.debug("security context refreshed with user " + username);
288
        }
289
        repo().commitTransaction(txStatus);
290
        return newAuthorityAdded ? authority : null;
291

    
292
    }
293

    
294
    /**
295
     * @param username
296
     * @param cdmType
297
     * @param entitiyId
298
     * @param crud
299
     * @return
300
     * @deprecated not performance optimized by using the cache,
301
     * use {@link #createAuthorityFor(String, Class, UUID, EnumSet, String)} instead
302
     */
303
    @Override
304
    @Deprecated
305
    public CdmAuthority createAuthorityFor(String username, Class<? extends CdmBase> cdmType, Integer entitiyId, EnumSet<CRUD> crud, String property) {
306

    
307
        CdmBase cdmEntity = repo().getCommonService().find(cdmType, entitiyId);
308
        return createAuthorityFor(username, cdmEntity, crud, property);
309
    }
310

    
311
    /**
312
     * @param username
313
     * @param cdmType
314
     * @param entitiyUuid
315
     * @param crud
316
     * @return
317
     */
318
    @Override
319
    public CdmAuthority createAuthorityFor(String username, Class<? extends CdmBase> cdmType, UUID entitiyUuid, EnumSet<CRUD> crud, String property) {
320

    
321
        CdmBase cdmEntity = entity(cdmType, entitiyUuid);
322
        return createAuthorityFor(username, cdmEntity, crud, property);
323
    }
324

    
325
    /**
326
     * {@inheritDoc}
327
     */
328
    @Override
329
    public CdmAuthority createAuthorityForCurrentUser(CdmBase cdmEntity, EnumSet<CRUD> crud, String property) {
330
        return createAuthorityFor(userName(), cdmEntity, crud, property);
331

    
332
    }
333

    
334
    /**
335
     * @param cdmType
336
     * @param entitiyId
337
     * @param crud
338
     * @return
339
     */
340
    @Override
341
    public CdmAuthority createAuthorityForCurrentUser(Class<? extends CdmBase> cdmType, Integer entitiyId, EnumSet<CRUD> crud, String property) {
342
        return createAuthorityFor(userName(), cdmType, entitiyId, crud, property);
343
    }
344

    
345
    /**
346
     * @param cdmType
347
     * @param entitiyUuid
348
     * @param crud
349
     * @return
350
     */
351
    @Override
352
    public CdmAuthority createAuthorityForCurrentUser(Class<? extends CdmBase> cdmType, UUID entitiyUuid, EnumSet<CRUD> crud, String property) {
353
        return createAuthorityFor(userName(), cdmType, entitiyUuid, crud, property);
354
    }
355

    
356
    /**
357
     * {@inheritDoc}
358
     */
359
    @Override
360
    public void removeAuthorityForCurrentUser(CdmAuthority cdmAuthority) {
361
        removeAuthorityForCurrentUser(userName(), cdmAuthority);
362

    
363
    }
364

    
365
    /**
366
     * {@inheritDoc}
367
     */
368
    @Override
369
    public void removeAuthorityForCurrentUser(String username, CdmAuthority cdmAuthority) {
370

    
371
        UserDetails userDetails = repo().getUserService().loadUserByUsername(username);
372
        if(userDetails != null){
373
            getRunAsAutheticator().runAsAuthentication(Role.ROLE_USER_MANAGER);
374
            User user = (User)userDetails;
375
            user.getGrantedAuthorities().remove(cdmAuthority);
376
            repo().getSession().flush();
377
            getRunAsAutheticator().restoreAuthentication();
378
            Authentication authentication = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
379
            SecurityContextHolder.getContext().setAuthentication(authentication);
380
            logger.debug("security context refreshed with user " + username);
381
        }
382

    
383
    }
384

    
385
    /**
386
     * {@inheritDoc}
387
     */
388
    @Override
389
    public Collection<CdmAuthority> findUserPermissions(CdmBase cdmEntity, EnumSet<CRUD> crud) {
390
        Set<CdmAuthority> matches = new HashSet<>();
391
        CdmPermissionClass permissionClass = CdmPermissionClass.getValueOf(cdmEntity);
392
        Collection<? extends GrantedAuthority> authorities = getAuthentication().getAuthorities();
393
        for(GrantedAuthority ga : authorities){
394
            try {
395
                CdmAuthority cdmAuthority = CdmAuthority.fromGrantedAuthority(ga);
396
                if(cdmAuthority.getPermissionClass().equals(permissionClass)){
397
                    if(cdmAuthority.getOperation().containsAll(crud)){
398
                        if(cdmAuthority.hasTargetUuid() && cdmAuthority.getTargetUUID().equals(cdmEntity.getUuid())){
399
                            matches.add(cdmAuthority);
400
                        } else {
401
                            matches.add(cdmAuthority);
402
                        }
403
                    }
404
                }
405
            } catch (CdmAuthorityParsingException e) {
406
                continue;
407
            }
408
        }
409
        return matches;
410
    }
411

    
412
    // @Override
413
    @Override
414
    public <T extends CdmBase> Collection<CdmAuthority> findUserPermissions(Class<T> cdmType, EnumSet<CRUD> crud) {
415
        Set<CdmAuthority> matches = new HashSet<>();
416
        CdmPermissionClass permissionClass = CdmPermissionClass.getValueOf(cdmType);
417
        Collection<? extends GrantedAuthority> authorities = getAuthentication().getAuthorities();
418
        for(GrantedAuthority ga : authorities){
419
            try {
420
                CdmAuthority cdmAuthority = CdmAuthority.fromGrantedAuthority(ga);
421
                if(cdmAuthority.getPermissionClass().equals(permissionClass)){
422
                    if(cdmAuthority.getOperation().containsAll(crud)){
423
                        matches.add(cdmAuthority);
424
                    }
425
                }
426
            } catch (CdmAuthorityParsingException e) {
427
                continue;
428
            }
429
        }
430
        return matches;
431
    }
432

    
433
    /**
434
     * @param securityContextAccess the securityContextAccess to set
435
     */
436
    @Override
437
    public void setSecurityContextAccess(SecurityContextAccess securityContextAccess) {
438
        this.securityContextAccess = securityContextAccess;
439
    }
440

    
441
    /**
442
     * @return the runAsAutheticator
443
     */
444
    public RunAsAuthenticator getRunAsAutheticator() {
445
        if(runAsAutheticator == null){
446
          throw new RuntimeException("RunAsAuthenticator is missing! The application needs to be configured with security context.");
447
        }
448
        return runAsAutheticator;
449
    }
450

    
451
    /**
452
     * @return the cache
453
     */
454
    public ICdmEntityUuidCacher getCache() {
455
        return null;
456
    }
457

    
458
    /**
459
     * @param cdmType
460
     * @param entitiyUuid
461
     * @return
462
     */
463
    private CdmBase entityFromCache(Class<? extends CdmBase> cdmType, UUID entitiyUuid) {
464
        CdmBase entity = null;
465
        if(getCache() != null){
466
           entity = getCache().getFromCache(entitiyUuid);
467
           if(entity != null && !cdmType.isAssignableFrom(entity.getClass())){
468
               logger.error("Entity with " +  entitiyUuid + " does not match the required type");
469
               entity = null;
470
           }
471
        }
472
        return entity;
473
    }
474

    
475
    @Override
476
    public CdmUserHelper withCache(ICdmEntityUuidCacher cache){
477

    
478
        return new CachingCdmUserHelper(cache);
479
    }
480

    
481
    class CachingCdmUserHelper extends CdmUserHelper{
482

    
483

    
484
        private static final long serialVersionUID = -5010082174809972831L;
485

    
486
        private ICdmEntityUuidCacher cache;
487

    
488
        public CachingCdmUserHelper(ICdmEntityUuidCacher cache){
489
            this.cache = cache;
490
        }
491

    
492
        /**
493
         * @return the cache
494
         */
495
        @Override
496
        public ICdmEntityUuidCacher getCache() {
497
            return cache;
498
        }
499

    
500
        @Override
501
        protected CdmRepository repo() {
502
            return CdmUserHelper.this.repo;
503
        }
504

    
505
        @Override
506
        protected ICdmPermissionEvaluator permissionEvaluator() {
507
            return CdmUserHelper.this.permissionEvaluator;
508
        }
509

    
510
    }
511

    
512
}
(1-1/12)