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