Merged the latest changes from the trunk into the branch ui-gwt
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / UserService.java
1 // $Id$
2 /**
3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 */
10 package eu.etaxonomy.cdm.api.service;
11
12 import java.util.ArrayList;
13 import java.util.Collection;
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.dao.DataAccessException;
22 import org.springframework.dao.IncorrectResultSizeDataAccessException;
23 import org.springframework.security.access.AccessDeniedException;
24 import org.springframework.security.access.prepost.PreAuthorize;
25 import org.springframework.security.authentication.AuthenticationManager;
26 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
27 import org.springframework.security.authentication.dao.SaltSource;
28 import org.springframework.security.authentication.encoding.PasswordEncoder;
29 import org.springframework.security.core.Authentication;
30 import org.springframework.security.core.GrantedAuthority;
31 import org.springframework.security.core.context.SecurityContextHolder;
32 import org.springframework.security.core.userdetails.UserCache;
33 import org.springframework.security.core.userdetails.UserDetails;
34 import org.springframework.security.core.userdetails.UsernameNotFoundException;
35 import org.springframework.security.core.userdetails.cache.NullUserCache;
36 import org.springframework.stereotype.Service;
37 import org.springframework.transaction.annotation.Propagation;
38 import org.springframework.transaction.annotation.Transactional;
39 import org.springframework.util.Assert;
40
41 import eu.etaxonomy.cdm.model.common.GrantedAuthorityImpl;
42 import eu.etaxonomy.cdm.model.common.Group;
43 import eu.etaxonomy.cdm.model.common.User;
44 import eu.etaxonomy.cdm.persistence.dao.common.IGrantedAuthorityDao;
45 import eu.etaxonomy.cdm.persistence.dao.common.IGroupDao;
46 import eu.etaxonomy.cdm.persistence.dao.common.IUserDao;
47 import eu.etaxonomy.cdm.persistence.query.MatchMode;
48 import eu.etaxonomy.cdm.persistence.query.OrderHint;
49
50 /**
51 * Note: All group related functionality has been refactored into a GroupService. 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 private UserCache userCache = new NullUserCache();
69
70 @Autowired(required = false)
71 public void setUserCache(UserCache userCache) {
72 Assert.notNull(userCache, "userCache cannot be null");
73 this.userCache = userCache;
74 }
75
76 @Autowired(required = false)
77 public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
78
79 this.passwordEncoder = passwordEncoder;
80 }
81
82 @Autowired(required = false)
83 public void setSaltSource(SaltSource saltSource) {
84 this.saltSource = saltSource;
85 }
86
87 @Autowired(required= false)
88 public void setAuthenticationManager(AuthenticationManager authenticationManager) {
89 this.authenticationManager = authenticationManager;
90 }
91
92 @Override
93 @Autowired
94 protected void setDao(IUserDao dao) {
95 this.dao = dao;
96 }
97
98 @Autowired
99 public void setGroupDao(IGroupDao groupDao) {
100 this.groupDao = groupDao;
101 }
102
103 @Autowired
104 public void setGrantedAuthorityDao(IGrantedAuthorityDao grantedAuthorityDao) {
105 this.grantedAuthorityDao = grantedAuthorityDao;
106 }
107
108 @Transactional(readOnly=false)
109 protected Authentication createNewAuthentication(Authentication currentAuth, String newPassword) {
110 UserDetails user = loadUserByUsername(currentAuth.getName());
111
112 UsernamePasswordAuthenticationToken newAuthentication = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
113 newAuthentication.setDetails(currentAuth.getDetails());
114
115 return newAuthentication;
116 }
117
118 @Override
119 @Transactional(readOnly=false)
120 @PreAuthorize("isAuthenticated()")
121 public void changePassword(String oldPassword, String newPassword) {
122 Assert.hasText(oldPassword);
123 Assert.hasText(newPassword);
124 Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
125 if(authentication != null && authentication.getPrincipal() != null && authentication.getPrincipal() instanceof User) {
126 User user = (User)authentication.getPrincipal();
127
128 authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), oldPassword));
129
130 Object salt = this.saltSource.getSalt(user);
131
132 String password = passwordEncoder.encodePassword(newPassword, salt);
133 ((User)user).setPassword(password);
134
135 dao.update((User)user);
136 SecurityContextHolder.getContext().setAuthentication(createNewAuthentication(authentication, newPassword));
137 userCache.removeUserFromCache(user.getUsername());
138 } else {
139 throw new AccessDeniedException("Can't change password as no Authentication object found in context for current user.");
140 }
141 }
142
143 /* (non-Javadoc)
144 * @see eu.etaxonomy.cdm.api.service.IUserService#changePasswordForUser(java.lang.String, java.lang.String)
145 */
146 @Override
147 @Transactional(readOnly=false)
148 @PreAuthorize("#username == authentication.name or hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
149 public void changePasswordForUser(String username, String newPassword) {
150 Assert.hasText(username);
151 Assert.hasText(newPassword);
152
153 try {
154 User user = dao.findUserByUsername(username);
155 if(user == null) {
156 throw new UsernameNotFoundException(username);
157 }
158
159 Object salt = this.saltSource.getSalt(user);
160
161 String password = passwordEncoder.encodePassword(newPassword, salt);
162 ((User)user).setPassword(password);
163
164 dao.update((User)user);
165 userCache.removeUserFromCache(user.getUsername());
166 } catch(NonUniqueResultException nure) {
167 throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username + "'", 1);
168 }
169 }
170
171 /* (non-Javadoc)
172 * @see org.springframework.security.provisioning.UserDetailsManager#createUser(org.springframework.security.core.userdetails.UserDetails)
173 */
174 @Override
175 @Transactional(readOnly=false)
176 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
177 public void createUser(UserDetails user) {
178 Assert.isInstanceOf(User.class, user);
179
180 String rawPassword = user.getPassword();
181 Object salt = this.saltSource.getSalt(user);
182
183 String password = passwordEncoder.encodePassword(rawPassword, salt);
184 ((User)user).setPassword(password);
185
186 dao.save((User)user);
187 }
188
189 /* (non-Javadoc)
190 * @see org.springframework.security.provisioning.UserDetailsManager#deleteUser(java.lang.String)
191 */
192 @Override
193 @Transactional(readOnly=false)
194 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
195 public void deleteUser(String username) {
196 Assert.hasLength(username);
197
198 User user = dao.findUserByUsername(username);
199 if(user != null) {
200 dao.delete((User)user);
201 }
202
203 userCache.removeUserFromCache(username);
204 }
205
206 /* (non-Javadoc)
207 * @see org.springframework.security.provisioning.UserDetailsManager#updateUser(org.springframework.security.core.userdetails.UserDetails)
208 */
209 @Override
210 @Transactional(readOnly=false)
211 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
212 public void updateUser(UserDetails user) {
213 Assert.isInstanceOf(User.class, user);
214
215 dao.update((User)user);
216 userCache.removeUserFromCache(user.getUsername());
217 }
218
219 /* (non-Javadoc)
220 * @see org.springframework.security.provisioning.UserDetailsManager#userExists(java.lang.String)
221 */
222 @Override
223 public boolean userExists(String username) {
224 Assert.hasText(username);
225
226 User user = dao.findUserByUsername(username);
227 return user != null;
228 }
229
230 /**
231 * <b>DO NOT CALL THIS METHOD IN LONG RUNNING SESSIONS OR CONVERSATIONS
232 * A THROWN UsernameNotFoundException WILL RENDER THE CONVERSATION UNUSABLE</b>
233 *
234 * @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
235 */
236 // NOTE: this method must not be secured since it is being used during the
237 // authentication process
238 public UserDetails loadUserByUsername(String username)
239 throws UsernameNotFoundException, DataAccessException {
240 Assert.hasText(username);
241 try {
242 User user = dao.findUserByUsername(username);
243 if(user == null) {
244 throw new UsernameNotFoundException(username);
245 }
246 return user;
247 } catch(NonUniqueResultException nure) {
248 throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username + "'", 1);
249 }
250 }
251
252 @Deprecated // use GroupService instead
253 @Transactional(readOnly=false)
254 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
255 public void addGroupAuthority(String groupName, GrantedAuthority authority) {
256 Assert.hasText(groupName);
257 Assert.notNull(authority);
258
259 Group group = groupDao.findGroupByName(groupName);
260 if(group.getGrantedAuthorities().add(authority)) {
261 groupDao.update(group);
262 }
263 }
264
265 @Deprecated // use GroupService instead
266 @Transactional(readOnly=false)
267 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
268 public void addUserToGroup(String username, String groupName) {
269 Assert.hasText(username);
270 Assert.hasText(groupName);
271
272 Group group = groupDao.findGroupByName(groupName);
273 User user = dao.findUserByUsername(username);
274
275 if(group.addMember(user)) {
276 groupDao.update(group);
277 userCache.removeUserFromCache(user.getUsername());
278 }
279 }
280
281 @Deprecated // use GroupService instead
282 @Transactional(readOnly=false)
283 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
284 public void createGroup(String groupName, List<GrantedAuthority> authorities) {
285 Assert.hasText(groupName);
286 Assert.notNull(authorities);
287
288 Group group = Group.NewInstance(groupName);
289
290 for(GrantedAuthority authority : authorities) {
291 group.getGrantedAuthorities().add(authority);
292 }
293
294 groupDao.save(group);
295 }
296
297 @Deprecated // use GroupService instead
298 @Transactional(readOnly=false)
299 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
300 public void deleteGroup(String groupName) {
301 Assert.hasText(groupName);
302
303 Group group = groupDao.findGroupByName(groupName);
304 groupDao.delete(group);
305 }
306
307 @Deprecated // use GroupService instead
308 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
309 public List<String> findAllGroups() {
310 return groupDao.listNames(null,null);
311 }
312
313 @Deprecated // use GroupService instead
314 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
315 public List<GrantedAuthority> findGroupAuthorities(String groupName) {
316 Assert.hasText(groupName);
317 Group group = groupDao.findGroupByName(groupName);
318
319 return new ArrayList<GrantedAuthority>(group.getGrantedAuthorities());
320 }
321
322 @Deprecated // use GroupService instead
323 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
324 public List<String> findUsersInGroup(String groupName) {
325 Assert.hasText(groupName);
326 Group group = groupDao.findGroupByName(groupName);
327
328 List<String> users = groupDao.listMembers(group, null, null);
329
330 return users;
331 }
332
333 @Deprecated // use GroupService instead
334 @Transactional(readOnly=false)
335 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
336 public void removeGroupAuthority(String groupName, GrantedAuthority authority) {
337 Assert.hasText(groupName);
338 Assert.notNull(authority);
339
340 Group group = groupDao.findGroupByName(groupName);
341
342 if(group.getGrantedAuthorities().remove(authority)) {
343 groupDao.update(group);
344 }
345 }
346
347 @Deprecated // use GroupService instead
348 @Transactional(readOnly=false)
349 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
350 public void removeUserFromGroup(String username, String groupName) {
351 Assert.hasText(username);
352 Assert.hasText(groupName);
353
354 Group group = groupDao.findGroupByName(groupName);
355 User user = dao.findUserByUsername(username);
356
357 if(group.removeMember(user)) {
358 groupDao.update(group);
359 userCache.removeUserFromCache(user.getUsername());
360 }
361 }
362
363 @Deprecated // use GroupService instead
364 @Transactional(readOnly=false)
365 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
366 public void renameGroup(String oldName, String newName) {
367 Assert.hasText(oldName);
368 Assert.hasText(newName);
369
370 Group group = groupDao.findGroupByName(oldName);
371
372 group.setName(newName);
373 groupDao.update(group);
374 }
375
376 @Transactional(readOnly=false)
377 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_RUN_AS_ADMIN') or hasRole('ROLE_USER_MANAGER')")
378 public UUID save(User user) {
379 if(user.getId() == 0 || dao.load(user.getUuid()) == null){
380 createUser(user);
381 }else{
382 updateUser(user);
383 }
384 return user.getUuid();
385 }
386
387 @Override
388 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
389 public UUID update(User user) {
390 updateUser(user);
391 return user.getUuid();
392 }
393
394 @Override
395 @Transactional(readOnly=false)
396 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
397 public UUID saveGrantedAuthority(GrantedAuthority grantedAuthority) {
398 return grantedAuthorityDao.save((GrantedAuthorityImpl)grantedAuthority);
399 }
400
401 @Deprecated // use GroupService instead
402 @Transactional(readOnly=false)
403 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
404 public UUID saveGroup(Group group) {
405 return groupDao.save(group);
406 }
407
408 /* (non-Javadoc)
409 * @see eu.etaxonomy.cdm.api.service.IUserService#listByUsername(java.lang.String, eu.etaxonomy.cdm.persistence.query.MatchMode, java.util.List, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
410 */
411 @Override
412 @Transactional(readOnly = true)
413 public List<User> listByUsername(String queryString,MatchMode matchmode, List<Criterion> criteria, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
414 Integer numberOfResults = dao.countByUsername(queryString, matchmode, criteria);
415
416 List<User> results = new ArrayList<User>();
417 if(numberOfResults > 0) {
418 results = dao.findByUsername(queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
419 }
420 return results;
421 }
422
423 /* ================================================
424 * overriding methods to secure them
425 * via the type level annotation @PreAuthorize
426 * ================================================ */
427
428 @Override
429 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
430 public UUID delete(User persistentObject) {
431 return super.delete(persistentObject);
432 }
433
434 @Override
435 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
436 public Map<UUID, User> save(Collection<User> newInstances) {
437 return super.save(newInstances);
438 }
439
440 @Override
441 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
442 public UUID saveOrUpdate(User transientObject) {
443 return super.saveOrUpdate(transientObject);
444 }
445
446 @Override
447 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
448 public Map<UUID, User> saveOrUpdate(Collection<User> transientInstances) {
449 return super.saveOrUpdate(transientInstances);
450 }
451
452 }