cleanup
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / UserService.java
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.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.Transactional;
38 import org.springframework.util.Assert;
39
40 import eu.etaxonomy.cdm.model.common.GrantedAuthorityImpl;
41 import eu.etaxonomy.cdm.model.common.User;
42 import eu.etaxonomy.cdm.persistence.dao.common.IGrantedAuthorityDao;
43 import eu.etaxonomy.cdm.persistence.dao.common.IGroupDao;
44 import eu.etaxonomy.cdm.persistence.dao.common.IUserDao;
45 import eu.etaxonomy.cdm.persistence.query.MatchMode;
46 import eu.etaxonomy.cdm.persistence.query.OrderHint;
47
48 /**
49 * Note: All group related functionality has been refactored into a GroupService.
50 * The will be removed in a future version.
51 */
52 @Service
53 @Transactional(readOnly = true)
54 // NOTE: no type level @PreAuthorize annotation for this class!
55 public class UserService extends ServiceBase<User,IUserDao> implements IUserService {
56
57 protected IGroupDao groupDao;
58
59 protected IGrantedAuthorityDao grantedAuthorityDao;
60
61 private SaltSource saltSource; // = new ReflectionSaltSource();
62
63 private PasswordEncoder passwordEncoder; // = new Md5PasswordEncoder();
64
65 private AuthenticationManager authenticationManager;
66
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
109 /**
110 * Changes the own password of in the database of the user which is
111 * currently authenticated. Requires to supply the old password for security
112 * reasons. Refreshes the authentication in the SecurityContext after the
113 * password change by re-authenticating the user with the new password.
114 *
115 * @see org.springframework.security.provisioning.UserDetailsManager#changePassword(java.lang.String,
116 * java.lang.String)
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
127 // get current authentication and load it from the persistence layer,
128 // to make sure we are modifying the instance which is
129 // attached to the hibernate session
130 User user = (User)authentication.getPrincipal();
131 user = dao.load(user.getUuid());
132
133 // check if old password is valid
134 authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), oldPassword));
135
136 // make new password and set it
137 Object salt = this.saltSource.getSalt(user);
138 String password = passwordEncoder.encodePassword(newPassword, salt);
139 user.setPassword(password);
140 dao.update(user);
141
142 // authenticate the user again with the new password
143 UsernamePasswordAuthenticationToken newAuthentication = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
144 newAuthentication.setDetails(authentication.getDetails());
145 SecurityContextHolder.getContext().setAuthentication(newAuthentication);
146 userCache.removeUserFromCache(user.getUsername());
147
148 } else {
149 throw new AccessDeniedException("Can't change password as no Authentication object found in context for current user.");
150 }
151 }
152
153 @Override
154 @Transactional(readOnly=false)
155 @PreAuthorize("#username == authentication.name or hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
156 public void changePasswordForUser(String username, String newPassword) {
157 Assert.hasText(username);
158 Assert.hasText(newPassword);
159
160 try {
161 User user = dao.findUserByUsername(username);
162 if(user == null) {
163 throw new UsernameNotFoundException(username);
164 }
165
166 Object salt = this.saltSource.getSalt(user);
167
168 String password = passwordEncoder.encodePassword(newPassword, salt);
169 user.setPassword(password);
170
171 dao.update(user);
172 userCache.removeUserFromCache(user.getUsername());
173 } catch(NonUniqueResultException nure) {
174 throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username + "'", 1);
175 }
176 }
177
178 @Override
179 @Transactional(readOnly=false)
180 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
181 public void createUser(UserDetails user) {
182 Assert.isInstanceOf(User.class, user);
183
184 String rawPassword = user.getPassword();
185 Object salt = this.saltSource.getSalt(user);
186
187 String password = passwordEncoder.encodePassword(rawPassword, salt);
188 ((User)user).setPassword(password);
189
190 dao.save((User)user);
191 }
192
193 @Override
194 @Transactional(readOnly=false)
195 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
196 public void deleteUser(String username) {
197 Assert.hasLength(username);
198
199 User user = dao.findUserByUsername(username);
200 if(user != null) {
201 dao.delete(user);
202 }
203
204 userCache.removeUserFromCache(username);
205 }
206
207 /* (non-Javadoc)
208 * @see org.springframework.security.provisioning.UserDetailsManager#updateUser(org.springframework.security.core.userdetails.UserDetails)
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 /* (non-Javadoc)
221 * @see org.springframework.security.provisioning.UserDetailsManager#userExists(java.lang.String)
222 */
223 @Override
224 public boolean userExists(String username) {
225 Assert.hasText(username);
226
227 User user = dao.findUserByUsername(username);
228 return user != null;
229 }
230
231 /**
232 * <b>DO NOT CALL THIS METHOD IN LONG RUNNING SESSIONS OR CONVERSATIONS
233 * A THROWN UsernameNotFoundException WILL RENDER THE CONVERSATION UNUSABLE</b>
234 *
235 * @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
236 */
237 // NOTE: this method must not be secured since it is being used during the
238 // authentication process
239 @Override
240 public UserDetails loadUserByUsername(String username)
241 throws UsernameNotFoundException, DataAccessException {
242 Assert.hasText(username);
243 try {
244 User user = dao.findUserByUsername(username);
245 if(user == null) {
246 throw new UsernameNotFoundException(username);
247 }
248 return user;
249 } catch(NonUniqueResultException nure) {
250 throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username + "'", 1);
251 }
252 }
253
254
255 @Override
256 @Transactional(readOnly=false)
257 // @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_RUN_AS_ADMIN') or hasRole('ROLE_USER_MANAGER')")
258 public User save(User user) {
259 if(user.getId() == 0 || dao.load(user.getUuid()) == null){
260 createUser(user);
261 }else{
262 updateUser(user);
263 }
264 return user;
265 }
266
267 @Override
268 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
269 public UUID update(User user) {
270 updateUser(user);
271 return user.getUuid();
272 }
273
274 @Override
275 @Transactional(readOnly=false)
276 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
277 public UUID saveGrantedAuthority(GrantedAuthority grantedAuthority) {
278 return grantedAuthorityDao.save((GrantedAuthorityImpl)grantedAuthority).getUuid();
279 }
280
281
282
283 @Override
284 @Transactional(readOnly = true)
285 public List<User> listByUsername(String queryString,MatchMode matchmode, List<Criterion> criteria, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
286 long numberOfResults = dao.countByUsername(queryString, matchmode, criteria);
287
288 List<User> results = new ArrayList<>();
289 if(numberOfResults > 0) {
290 results = dao.findByUsername(queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
291 }
292 return results;
293 }
294
295 /* ================================================
296 * overriding methods to secure them
297 * via the type level annotation @PreAuthorize
298 * ================================================ */
299
300 @Override
301 @Transactional(readOnly=false)
302 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
303 public DeleteResult delete(User persistentObject) {
304 return super.delete(persistentObject);
305 }
306
307 @Override
308 @Transactional(readOnly=false)
309 public DeleteResult delete(UUID userUuid) {
310 return delete(dao.load(userUuid));
311 }
312
313 @Override
314 @Transactional(readOnly=false)
315 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
316 public Map<UUID, User> save(Collection<User> newInstances) {
317 Map<UUID, User> users = new HashMap<UUID, User>();
318 for (User user: newInstances){
319 createUser(user);
320 users.put(user.getUuid(), user);
321 }
322 return users;
323 }
324
325 @Override
326 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
327 public UUID saveOrUpdate(User transientObject) {
328 return super.saveOrUpdate(transientObject);
329 }
330
331 @Override
332 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
333 @Transactional(readOnly=false)
334 public User merge(User detachedObject) {
335 return super.merge(detachedObject);
336 }
337
338 @Override
339 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
340 @Transactional(readOnly=false)
341 public List<User> merge(List<User> detachedObjects) {
342 return super.merge(detachedObjects);
343 }
344
345 @Override
346 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
347 public Map<UUID, User> saveOrUpdate(Collection<User> transientInstances) {
348 return super.saveOrUpdate(transientInstances);
349 }
350
351 }