Project

General

Profile

Download (13.1 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
 * Copyright (C) 2007 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.service;
10

    
11
import java.util.ArrayList;
12
import java.util.Collection;
13
import java.util.HashMap;
14
import java.util.List;
15
import java.util.Map;
16
import java.util.UUID;
17

    
18
import org.hibernate.NonUniqueResultException;
19
import org.hibernate.criterion.Criterion;
20
import org.springframework.beans.factory.annotation.Autowired;
21
import org.springframework.context.annotation.Lazy;
22
import org.springframework.dao.DataAccessException;
23
import org.springframework.dao.IncorrectResultSizeDataAccessException;
24
import org.springframework.security.access.AccessDeniedException;
25
import org.springframework.security.access.prepost.PreAuthorize;
26
import org.springframework.security.authentication.AuthenticationManager;
27
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
28
import org.springframework.security.authentication.dao.SaltSource;
29
import org.springframework.security.authentication.encoding.PasswordEncoder;
30
import org.springframework.security.core.Authentication;
31
import org.springframework.security.core.GrantedAuthority;
32
import org.springframework.security.core.context.SecurityContextHolder;
33
import org.springframework.security.core.userdetails.UserCache;
34
import org.springframework.security.core.userdetails.UserDetails;
35
import org.springframework.security.core.userdetails.UsernameNotFoundException;
36
import org.springframework.security.core.userdetails.cache.NullUserCache;
37
import org.springframework.stereotype.Service;
38
import org.springframework.transaction.annotation.Transactional;
39
import org.springframework.util.Assert;
40

    
41
import eu.etaxonomy.cdm.model.permission.GrantedAuthorityImpl;
42
import eu.etaxonomy.cdm.model.permission.User;
43
import eu.etaxonomy.cdm.persistence.dao.permission.IGrantedAuthorityDao;
44
import eu.etaxonomy.cdm.persistence.dao.permission.IGroupDao;
45
import eu.etaxonomy.cdm.persistence.dao.permission.IUserDao;
46
import eu.etaxonomy.cdm.persistence.query.MatchMode;
47
import eu.etaxonomy.cdm.persistence.query.OrderHint;
48

    
49
/**
50
 * Note: All group related functionality has been refactored into a GroupService.
51
 * The will be removed in a future version.
52
 */
53
@Service
54
@Transactional(readOnly = true)
55
// NOTE: no type level @PreAuthorize annotation for this class!
56
public class UserService extends ServiceBase<User,IUserDao> implements IUserService {
57

    
58
    protected IGroupDao groupDao;
59

    
60
    protected IGrantedAuthorityDao grantedAuthorityDao;
61

    
62
    private SaltSource saltSource; // = new ReflectionSaltSource();
63

    
64
    private PasswordEncoder passwordEncoder; // = new Md5PasswordEncoder();
65

    
66
    private AuthenticationManager authenticationManager;
67

    
68

    
69
    private UserCache userCache = new NullUserCache();
70

    
71
    @Autowired(required = false)
72
    public void setUserCache(UserCache userCache) {
73
        Assert.notNull(userCache, "userCache cannot be null");
74
        this.userCache = userCache;
75
    }
76

    
77
    @Autowired(required = false)
78
    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
79

    
80
        this.passwordEncoder = passwordEncoder;
81
    }
82

    
83
    @Autowired(required = false)
84
    public void setSaltSource(SaltSource saltSource) {
85
        this.saltSource = saltSource;
86
    }
87

    
88
    @Autowired(required= false)
89
    @Lazy // avoid dependency cycle coming from OAuth2ServerConfiguration.AuthorizationServerConfiguration.authenticationManager
90
    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
91
        this.authenticationManager = authenticationManager;
92
    }
93

    
94
    @Override
95
    @Autowired
96
    protected void setDao(IUserDao dao) {
97
        this.dao = dao;
98
    }
99

    
100
    @Autowired
101
    public void setGroupDao(IGroupDao groupDao) {
102
        this.groupDao = groupDao;
103
    }
104

    
105
    @Autowired
106
    public void setGrantedAuthorityDao(IGrantedAuthorityDao grantedAuthorityDao) {
107
        this.grantedAuthorityDao = grantedAuthorityDao;
108
    }
109

    
110

    
111
    /**
112
     * Changes the own password of in the database of the user which is
113
     * currently authenticated. Requires to supply the old password for security
114
     * reasons. Refreshes the authentication in the SecurityContext after the
115
     * password change by re-authenticating the user with the new password.
116
     *
117
     * @see org.springframework.security.provisioning.UserDetailsManager#changePassword(java.lang.String,
118
     *      java.lang.String)
119
     */
120
    @Override
121
    @Transactional(readOnly=false)
122
    @PreAuthorize("isAuthenticated()")
123
    public void changePassword(String oldPassword, String newPassword) {
124
        Assert.hasText(oldPassword);
125
        Assert.hasText(newPassword);
126
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
127
        if(authentication != null && authentication.getPrincipal() != null && authentication.getPrincipal() instanceof User) {
128

    
129
            // get current authentication and load it from the persistence layer,
130
            // to make sure we are modifying the instance which is
131
            // attached to the hibernate session
132
            User user = (User)authentication.getPrincipal();
133
            user = dao.load(user.getUuid());
134

    
135
            // check if old password is valid
136
            authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), oldPassword));
137
            encodeUserPassword(user, newPassword);
138
            dao.update(user);
139

    
140
            // authenticate the user again with the new password
141
            UsernamePasswordAuthenticationToken newAuthentication = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
142
            newAuthentication.setDetails(authentication.getDetails());
143
            SecurityContextHolder.getContext().setAuthentication(newAuthentication);
144
            userCache.removeUserFromCache(user.getUsername());
145

    
146
        } else {
147
            throw new AccessDeniedException("Can't change password as no Authentication object found in context for current user.");
148
        }
149
    }
150

    
151
    /**
152
     * make new password salt, encode and set it for the passed user
153
     *
154
     * @param user
155
     *  The user to set the new password for.
156
     * @param newPassword
157
     *  the new password to be encoded and set for the <code>user</code>
158
     */
159
    @Override
160
    public void encodeUserPassword(User user, String newPassword) {
161
        Object salt = this.saltSource.getSalt(user);
162
        String password = passwordEncoder.encodePassword(newPassword, salt);
163
        user.setPassword(password);
164
    }
165

    
166
    @Override
167
    @Transactional(readOnly=false)
168
    @PreAuthorize("#username == authentication.name or hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
169
    public void changePasswordForUser(String username, String newPassword) {
170
        Assert.hasText(username);
171
        Assert.hasText(newPassword);
172

    
173
        try {
174
            User user = dao.findUserByUsername(username);
175
            if(user == null) {
176
                throw new UsernameNotFoundException(username);
177
            }
178

    
179
            encodeUserPassword(user, newPassword);
180
            dao.update(user);
181
            userCache.removeUserFromCache(user.getUsername());
182
        } catch(NonUniqueResultException nure) {
183
            throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username + "'", 1);
184
        }
185
    }
186

    
187
    @Override
188
    @Transactional(readOnly=false)
189
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
190
    public void createUser(UserDetails user) {
191
    	Assert.isInstanceOf(User.class, user);
192
        encodeUserPassword((User)user, user.getPassword());
193
        dao.save((User)user);
194
    }
195

    
196
    @Override
197
    @Transactional(readOnly=false)
198
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
199
    public void deleteUser(String username) {
200
        Assert.hasLength(username);
201

    
202
        User user = dao.findUserByUsername(username);
203
        if(user != null) {
204
            dao.delete(user);
205
        }
206

    
207
        userCache.removeUserFromCache(username);
208
    }
209

    
210
    @Override
211
    @Transactional(readOnly=false)
212
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
213
    public void updateUser(UserDetails user) {
214
        Assert.isInstanceOf(User.class, user);
215

    
216
        dao.update((User)user);
217
        userCache.removeUserFromCache(user.getUsername());
218
    }
219

    
220
    @Override
221
    public boolean userExists(String username) {
222
        Assert.hasText(username);
223

    
224
        User user = dao.findUserByUsername(username);
225
        return user != null;
226
    }
227

    
228
    /**
229
     * <b>DO NOT CALL THIS METHOD IN LONG RUNNING SESSIONS OR CONVERSATIONS
230
     * A THROWN UsernameNotFoundException WILL RENDER THE CONVERSATION UNUSABLE</b>
231
     *
232
     * @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
233
     */
234
    // NOTE: this method must not be secured since it is being used during the
235
    //       authentication process
236
    @Override
237
    public UserDetails loadUserByUsername(String username)
238
            throws UsernameNotFoundException, DataAccessException {
239
        Assert.hasText(username);
240
        try {
241
            User user = dao.findUserByUsername(username);
242
            if(user == null) {
243
                throw new UsernameNotFoundException(username);
244
            }
245
            return user;
246
        } catch(NonUniqueResultException nure) {
247
            throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username + "'", 1);
248
        }
249
    }
250

    
251

    
252
    @Override
253
    @Transactional(readOnly=false)
254
   // @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_RUN_AS_ADMIN') or hasRole('ROLE_USER_MANAGER')")
255
    public User save(User user) {
256
        if(user.getId() == 0 || dao.load(user.getUuid()) == null){
257
            createUser(user);
258
        }else{
259
            updateUser(user);
260
        }
261
        return user;
262
    }
263

    
264
    @Override
265
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
266
    public UUID update(User user) {
267
        updateUser(user);
268
        return user.getUuid();
269
    }
270

    
271
    @Override
272
    @Transactional(readOnly=false)
273
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
274
    public UUID saveGrantedAuthority(GrantedAuthority grantedAuthority) {
275
        return grantedAuthorityDao.save((GrantedAuthorityImpl)grantedAuthority).getUuid();
276
    }
277

    
278

    
279

    
280
    @Override
281
    @Transactional(readOnly = true)
282
    public List<User> listByUsername(String queryString,MatchMode matchmode, List<Criterion> criteria, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
283
         long numberOfResults = dao.countByUsername(queryString, matchmode, criteria);
284

    
285
         List<User> results = new ArrayList<>();
286
         if(numberOfResults > 0) {
287
                results = dao.findByUsername(queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
288
         }
289
         return results;
290
    }
291

    
292
    /* ================================================
293
     *  overriding methods to secure them
294
     *  via the type level annotation @PreAuthorize
295
     * ================================================ */
296

    
297
    @Override
298
    @Transactional(readOnly=false)
299
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
300
    public DeleteResult delete(User persistentObject)  {
301
        return super.delete(persistentObject);
302
    }
303

    
304
    @Override
305
    @Transactional(readOnly=false)
306
    public DeleteResult delete(UUID userUuid)  {
307
        return delete(dao.load(userUuid));
308
    }
309

    
310
    @Override
311
    @Transactional(readOnly=false)
312
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
313
    public Map<UUID, User> save(Collection<User> newInstances) {
314
        Map<UUID, User> users = new HashMap<UUID, User>();
315
    	for (User user: newInstances){
316
        	createUser(user);
317
        	users.put(user.getUuid(), user);
318
        }
319
    	return users;
320
    }
321

    
322
    @Override
323
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
324
    public UUID saveOrUpdate(User transientObject) {
325
        return super.saveOrUpdate(transientObject);
326
    }
327

    
328
    @Override
329
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
330
    @Transactional(readOnly=false)
331
    public User merge(User detachedObject) {
332
        return super.merge(detachedObject);
333
    }
334

    
335
    @Override
336
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
337
    @Transactional(readOnly=false)
338
    public List<User> merge(List<User> detachedObjects) {
339
        return super.merge(detachedObjects);
340
    }
341

    
342
    @Override
343
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
344
    public Map<UUID, User> saveOrUpdate(Collection<User> transientInstances) {
345
        return super.saveOrUpdate(transientInstances);
346
    }
347

    
348
}
(93-93/95)