bugfix for updateTitleCache
[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.List;
14 import java.util.UUID;
15
16 import org.hibernate.NonUniqueResultException;
17 import org.springframework.beans.factory.annotation.Autowired;
18 import org.springframework.dao.DataAccessException;
19 import org.springframework.dao.IncorrectResultSizeDataAccessException;
20
21 import org.springframework.security.access.AccessDeniedException;
22 import org.springframework.security.authentication.AuthenticationManager;
23 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
24 import org.springframework.security.authentication.dao.ReflectionSaltSource;
25 import org.springframework.security.authentication.dao.SaltSource;
26 import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
27 import org.springframework.security.authentication.encoding.PasswordEncoder;
28 import org.springframework.security.core.Authentication;
29 import org.springframework.security.core.GrantedAuthority;
30 import org.springframework.security.core.context.SecurityContextHolder;
31 import org.springframework.security.core.userdetails.UserCache;
32 import org.springframework.security.core.userdetails.UserDetails;
33 import org.springframework.security.core.userdetails.UsernameNotFoundException;
34 import org.springframework.security.core.userdetails.cache.NullUserCache;
35 import org.springframework.stereotype.Service;
36 import org.springframework.transaction.annotation.Propagation;
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.Group;
42 import eu.etaxonomy.cdm.model.common.User;
43 import eu.etaxonomy.cdm.persistence.dao.common.IGrantedAuthorityDao;
44 import eu.etaxonomy.cdm.persistence.dao.common.IGroupDao;
45 import eu.etaxonomy.cdm.persistence.dao.common.IUserDao;
46
47 @Service
48 @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
49 public class UserService extends ServiceBase<User,IUserDao> implements IUserService {
50
51 protected IGroupDao groupDao;
52
53 protected IGrantedAuthorityDao grantedAuthorityDao;
54
55 private SaltSource saltSource = new ReflectionSaltSource();
56
57 private PasswordEncoder passwordEncoder = new Md5PasswordEncoder();
58
59 private AuthenticationManager authenticationManager;
60
61 private UserCache userCache = new NullUserCache();
62
63 @Autowired(required = false)
64 public void setUserCache(UserCache userCache) {
65 Assert.notNull(userCache, "userCache cannot be null");
66 this.userCache = userCache;
67 }
68
69 @Autowired(required = false)
70 public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
71
72 this.passwordEncoder = passwordEncoder;
73 }
74
75 @Autowired(required = false)
76 public void setSaltSource(SaltSource saltSource) {
77 this.saltSource = saltSource;
78 }
79
80 @Autowired(required= false)
81 public void setAuthenticationManager(AuthenticationManager authenticationManager) {
82 this.authenticationManager = authenticationManager;
83 }
84
85 @Override
86 @Autowired
87 protected void setDao(IUserDao dao) {
88 this.dao = dao;
89 }
90
91 @Autowired
92 public void setGroupDao(IGroupDao groupDao) {
93 this.groupDao = groupDao;
94 }
95
96 @Autowired
97 public void setGrantedAuthorityDao(IGrantedAuthorityDao grantedAuthorityDao) {
98 this.grantedAuthorityDao = grantedAuthorityDao;
99 }
100
101 @Transactional(readOnly=false)
102 protected Authentication createNewAuthentication(Authentication currentAuth, String newPassword) {
103 UserDetails user = loadUserByUsername(currentAuth.getName());
104
105 UsernamePasswordAuthenticationToken newAuthentication = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
106 newAuthentication.setDetails(currentAuth.getDetails());
107
108 return newAuthentication;
109 }
110
111 @Transactional(readOnly=false)
112 public void changePassword(String oldPassword, String newPassword) {
113 Assert.hasText(oldPassword);
114 Assert.hasText(newPassword);
115 Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
116 if(authentication != null && authentication.getPrincipal() != null && authentication.getPrincipal() instanceof User) {
117 User user = (User)authentication.getPrincipal();
118
119 authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), oldPassword));
120
121 Object salt = this.saltSource.getSalt(user);
122
123 String password = passwordEncoder.encodePassword(newPassword, salt);
124 ((User)user).setPassword(password);
125
126 dao.update((User)user);
127 SecurityContextHolder.getContext().setAuthentication(createNewAuthentication(authentication, newPassword));
128 userCache.removeUserFromCache(user.getUsername());
129 } else {
130 throw new AccessDeniedException("Can't change password as no Authentication object found in context for current user.");
131 }
132 }
133
134 @Transactional(readOnly=false)
135 public void changePasswordForUser(String username, String newPassword) {
136 Assert.hasText(username);
137 Assert.hasText(newPassword);
138
139 try {
140 User user = dao.findUserByUsername(username);
141 if(user == null) {
142 throw new UsernameNotFoundException(username);
143 }
144
145 Object salt = this.saltSource.getSalt(user);
146
147 String password = passwordEncoder.encodePassword(newPassword, salt);
148 ((User)user).setPassword(password);
149
150 dao.update((User)user);
151 userCache.removeUserFromCache(user.getUsername());
152 } catch(NonUniqueResultException nure) {
153 throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username + "'", 1);
154 }
155 }
156
157 @Transactional(readOnly=false)
158 public void createUser(UserDetails user) {
159 Assert.isInstanceOf(User.class, user);
160
161 String rawPassword = user.getPassword();
162 Object salt = this.saltSource.getSalt(user);
163
164 String password = passwordEncoder.encodePassword(rawPassword, salt);
165 ((User)user).setPassword(password);
166
167 dao.save((User)user);
168 }
169
170 @Transactional(readOnly=false)
171 public void deleteUser(String username) {
172 Assert.hasLength(username);
173
174 User user = dao.findUserByUsername(username);
175 if(user != null) {
176 dao.delete((User)user);
177 }
178
179 userCache.removeUserFromCache(username);
180 }
181
182 @Transactional(readOnly=false)
183 public void updateUser(UserDetails user) {
184 Assert.isInstanceOf(User.class, user);
185
186 dao.update((User)user);
187 userCache.removeUserFromCache(user.getUsername());
188 }
189
190 public boolean userExists(String username) {
191 Assert.hasText(username);
192
193 User user = dao.findUserByUsername(username);
194 return user != null;
195 }
196
197 /**
198 * DO NOT CALL THIS METHOD IN LONG RUNNING SESSIONS OR CONVERSATIONS
199 * A THROWN UsernameNotFoundException WILL RENDER THE CONVERSATION UNUSABLE
200 */
201 public UserDetails loadUserByUsername(String username)
202 throws UsernameNotFoundException, DataAccessException {
203 Assert.hasText(username);
204 try {
205 User user = dao.findUserByUsername(username);
206 if(user == null) {
207 throw new UsernameNotFoundException(username);
208 }
209 return user;
210 } catch(NonUniqueResultException nure) {
211 throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username + "'", 1);
212 }
213 }
214
215 @Transactional(readOnly=false)
216 public void addGroupAuthority(String groupName, GrantedAuthority authority) {
217 Assert.hasText(groupName);
218 Assert.notNull(authority);
219
220 Group group = groupDao.findGroupByName(groupName);
221 if(group.getGrantedAuthorities().add(authority)) {
222 groupDao.update(group);
223 }
224 }
225
226 @Transactional(readOnly=false)
227 public void addUserToGroup(String username, String groupName) {
228 Assert.hasText(username);
229 Assert.hasText(groupName);
230
231 Group group = groupDao.findGroupByName(groupName);
232 User user = dao.findUserByUsername(username);
233
234 if(group.addMember(user)) {
235 groupDao.update(group);
236 userCache.removeUserFromCache(user.getUsername());
237 }
238 }
239
240 @Transactional(readOnly=false)
241 public void createGroup(String groupName, List<GrantedAuthority> authorities) {
242 Assert.hasText(groupName);
243 Assert.notNull(authorities);
244
245 Group group = new Group();
246 group.setName(groupName);
247
248 for(GrantedAuthority authority : authorities) {
249 group.getGrantedAuthorities().add(authority);
250 }
251
252 groupDao.save(group);
253 }
254
255 @Transactional(readOnly=false)
256 public void deleteGroup(String groupName) {
257 Assert.hasText(groupName);
258
259 Group group = groupDao.findGroupByName(groupName);
260 groupDao.delete(group);
261 }
262
263 public List<String> findAllGroups() {
264 return groupDao.listNames(null,null);
265 }
266
267 public List<GrantedAuthority> findGroupAuthorities(String groupName) {
268 Assert.hasText(groupName);
269 Group group = groupDao.findGroupByName(groupName);
270
271 return new ArrayList<GrantedAuthority>(group.getGrantedAuthorities());
272 }
273
274 public List<String> findUsersInGroup(String groupName) {
275 Assert.hasText(groupName);
276 Group group = groupDao.findGroupByName(groupName);
277
278 List<String> users = groupDao.listMembers(group, null, null);
279
280 return users;
281 }
282
283 @Transactional(readOnly=false)
284 public void removeGroupAuthority(String groupName, GrantedAuthority authority) {
285 Assert.hasText(groupName);
286 Assert.notNull(authority);
287
288 Group group = groupDao.findGroupByName(groupName);
289
290 if(group.getGrantedAuthorities().remove(authority)) {
291 groupDao.update(group);
292 }
293 }
294
295 @Transactional(readOnly=false)
296 public void removeUserFromGroup(String username, String groupName) {
297 Assert.hasText(username);
298 Assert.hasText(groupName);
299
300 Group group = groupDao.findGroupByName(groupName);
301 User user = dao.findUserByUsername(username);
302
303 if(group.removeMember(user)) {
304 groupDao.update(group);
305 userCache.removeUserFromCache(user.getUsername());
306 }
307 }
308
309 @Transactional(readOnly=false)
310 public void renameGroup(String oldName, String newName) {
311 Assert.hasText(oldName);
312 Assert.hasText(newName);
313
314 Group group = groupDao.findGroupByName(oldName);
315
316 group.setName(newName);
317 groupDao.update(group);
318 }
319
320 @Transactional(readOnly=false)
321 public UUID save(User user) {
322 if(user.getId() == 0 || dao.load(user.getUuid()) == null){
323 createUser(user);
324 }else{
325 updateUser(user);
326 }
327 return user.getUuid();
328 }
329
330 @Override
331 public UUID update(User user) {
332 updateUser(user);
333 return user.getUuid();
334 }
335
336 @Transactional(readOnly=false)
337 public UUID saveGrantedAuthority(GrantedAuthority grantedAuthority) {
338 return grantedAuthorityDao.save((GrantedAuthorityImpl)grantedAuthority);
339 }
340
341 @Transactional(readOnly=false)
342 public UUID saveGroup(Group group) {
343 return groupDao.save(group);
344 }
345 }