ref #10222 add agentlink to taxonNodeAgentRelDto
[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.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.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 private IGrantedAuthorityDao grantedAuthorityDao;
58
59 private SaltSource saltSource; // = new ReflectionSaltSource();
60
61 private PasswordEncoder passwordEncoder; // = new Md5PasswordEncoder();
62
63 private AuthenticationManager authenticationManager;
64
65 private UserCache userCache = new NullUserCache();
66
67 @Autowired(required = false)
68 public void setUserCache(UserCache userCache) {
69 Assert.notNull(userCache, "userCache cannot be null");
70 this.userCache = userCache;
71 }
72
73 @Autowired(required = false)
74 public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
75 this.passwordEncoder = passwordEncoder;
76 }
77
78 @Autowired(required = false)
79 public void setSaltSource(SaltSource saltSource) {
80 this.saltSource = saltSource;
81 }
82
83 @Autowired(required= false)
84 @Lazy // avoid dependency cycle coming from OAuth2ServerConfiguration.AuthorizationServerConfiguration.authenticationManager
85 public void setAuthenticationManager(AuthenticationManager authenticationManager) {
86 this.authenticationManager = authenticationManager;
87 }
88
89 @Override
90 @Autowired
91 protected void setDao(IUserDao dao) {
92 this.dao = dao;
93 }
94
95 @Autowired
96 public void setGrantedAuthorityDao(IGrantedAuthorityDao grantedAuthorityDao) {
97 this.grantedAuthorityDao = grantedAuthorityDao;
98 }
99
100 /**
101 * Changes the own password of in the database of the user which is
102 * currently authenticated. Requires to supply the old password for security
103 * reasons. Refreshes the authentication in the SecurityContext after the
104 * password change by re-authenticating the user with the new password.
105 *
106 * @see org.springframework.security.provisioning.UserDetailsManager#changePassword(java.lang.String,
107 * java.lang.String)
108 */
109 @Override
110 @Transactional(readOnly=false)
111 @PreAuthorize("isAuthenticated()")
112 public void changePassword(String oldPassword, String newPassword) {
113 Assert.hasText(oldPassword, "Old password must not be empty.");
114 Assert.hasText(newPassword, "New password must not be empty.");
115 Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
116 if(authentication != null && authentication.getPrincipal() != null && authentication.getPrincipal() instanceof User) {
117
118 // get current authentication and load it from the persistence layer,
119 // to make sure we are modifying the instance which is
120 // attached to the hibernate session
121 User user = (User)authentication.getPrincipal();
122 user = dao.load(user.getUuid());
123
124 // check if old password is valid
125 authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), oldPassword));
126 encodeUserPassword(user, newPassword);
127 dao.update(user);
128
129 // authenticate the user again with the new password
130 UsernamePasswordAuthenticationToken newAuthentication = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
131 newAuthentication.setDetails(authentication.getDetails());
132 SecurityContextHolder.getContext().setAuthentication(newAuthentication);
133 userCache.removeUserFromCache(user.getUsername());
134
135 } else {
136 throw new AccessDeniedException("Can't change password as no Authentication object found in context for current user.");
137 }
138 }
139
140 /**
141 * Make new password salt, encode and set it for the passed user
142 *
143 * @param user
144 * The user to set the new password for.
145 * @param newPassword
146 * the new password to be encoded and set for the <code>user</code>
147 */
148 @Override
149 public void encodeUserPassword(User user, String newPassword) {
150 Object salt = this.saltSource.getSalt(user);
151 String password = passwordEncoder.encodePassword(newPassword, salt);
152 user.setPassword(password);
153 }
154
155 @Override
156 @Transactional(readOnly=false)
157 @PreAuthorize("#username == authentication.name or hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
158 public void changePasswordForUser(String username, String newPassword) {
159 Assert.hasText(username, "Username must not be empty.");
160 Assert.hasText(newPassword, "Password must not be empty.");
161
162 try {
163 User user = dao.findUserByUsername(username);
164 if(user == null) {
165 throw new UsernameNotFoundException(username);
166 }
167
168 encodeUserPassword(user, newPassword);
169 dao.update(user);
170 userCache.removeUserFromCache(user.getUsername());
171 } catch(NonUniqueResultException nure) {
172 throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username + "'", 1);
173 }
174 }
175
176 @Override
177 @Transactional(readOnly=false)
178 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
179 public void createUser(UserDetails user) {
180 Assert.isInstanceOf(User.class, user);
181 encodeUserPassword((User)user, user.getPassword());
182 dao.save((User)user);
183 }
184
185 @Override
186 @Transactional(readOnly=false)
187 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
188 public void deleteUser(String username) {
189 Assert.hasLength(username, "Username must not be empty.");
190
191 User user = dao.findUserByUsername(username);
192 if(user != null) {
193 dao.delete(user);
194 }
195
196 userCache.removeUserFromCache(username);
197 }
198
199 @Override
200 @Transactional(readOnly=false)
201 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
202 public void updateUser(UserDetails user) {
203 Assert.isInstanceOf(User.class, user);
204
205 dao.update((User)user);
206 userCache.removeUserFromCache(user.getUsername());
207 }
208
209 @Override
210 public boolean userExists(String username) {
211 Assert.hasText(username, "Parameter 'username' must not be empty.");
212
213 User user = dao.findUserByUsername(username);
214 return user != null;
215 }
216
217 /**
218 * <b>DO NOT CALL THIS METHOD IN LONG RUNNING SESSIONS OR CONVERSATIONS
219 * A THROWN UsernameNotFoundException WILL RENDER THE CONVERSATION UNUSABLE</b>
220 *
221 * @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
222 */
223 // NOTE: this method must not be secured since it is being used during the
224 // authentication process
225 @Override
226 public UserDetails loadUserByUsername(String username)
227 throws UsernameNotFoundException, DataAccessException {
228 Assert.hasText(username, "Username must not be empty.");
229 try {
230 User user = dao.findUserByUsername(username);
231 if(user == null) {
232 throw new UsernameNotFoundException(username);
233 }
234 return user;
235 } catch(NonUniqueResultException nure) {
236 throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username + "'", 1);
237 }
238 }
239
240 @Override
241 @Transactional(readOnly=false)
242 // @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_RUN_AS_ADMIN') or hasRole('ROLE_USER_MANAGER')")
243 public <S extends User> S save(S user) {
244 if(user.getId() == 0 || dao.load(user.getUuid()) == null){
245 createUser(user);
246 }else{
247 updateUser(user);
248 }
249 return user;
250 }
251
252 @Override
253 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
254 public UUID update(User user) {
255 updateUser(user);
256 return user.getUuid();
257 }
258
259 @Override
260 @Transactional(readOnly=false)
261 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
262 public UUID saveGrantedAuthority(GrantedAuthority grantedAuthority) {
263 return grantedAuthorityDao.save((GrantedAuthorityImpl)grantedAuthority).getUuid();
264 }
265
266
267
268 @Override
269 @Transactional(readOnly = true)
270 public List<User> listByUsername(String queryString,MatchMode matchmode, List<Criterion> criteria, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
271 long numberOfResults = dao.countByUsername(queryString, matchmode, criteria);
272
273 List<User> results = new ArrayList<>();
274 if(numberOfResults > 0) {
275 results = dao.findByUsername(queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
276 }
277 return results;
278 }
279
280 /* ================================================
281 * overriding methods to secure them
282 * via the type level annotation @PreAuthorize
283 * ================================================ */
284
285 @Override
286 @Transactional(readOnly=false)
287 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
288 public DeleteResult delete(User persistentObject) {
289 return super.delete(persistentObject);
290 }
291
292 @Override
293 @Transactional(readOnly=false)
294 public DeleteResult delete(UUID userUuid) {
295 return delete(dao.load(userUuid));
296 }
297
298 @Override
299 @Transactional(readOnly=false)
300 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
301 public Map<UUID, User> save(Collection<? extends User> newInstances) {
302 Map<UUID, User> users = new HashMap<UUID, User>();
303 for (User user: newInstances){
304 createUser(user);
305 users.put(user.getUuid(), user);
306 }
307 return users;
308 }
309
310 @Override
311 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
312 public UUID saveOrUpdate(User transientObject) {
313 return super.saveOrUpdate(transientObject);
314 }
315
316 @Override
317 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
318 @Transactional(readOnly=false)
319 public User merge(User detachedObject) {
320 return super.merge(detachedObject);
321 }
322
323 @Override
324 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
325 @Transactional(readOnly=false)
326 public List<User> merge(List<User> detachedObjects) {
327 return super.merge(detachedObjects);
328 }
329
330 @Override
331 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
332 public Map<UUID, User> saveOrUpdate(Collection<User> transientInstances) {
333 return super.saveOrUpdate(transientInstances);
334 }
335
336 }