3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.cdm
.api
.service
;
12 import java
.util
.ArrayList
;
13 import java
.util
.List
;
14 import java
.util
.UUID
;
16 import org
.hibernate
.NonUniqueResultException
;
17 import org
.hibernate
.criterion
.Criterion
;
18 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
19 import org
.springframework
.dao
.DataAccessException
;
20 import org
.springframework
.dao
.IncorrectResultSizeDataAccessException
;
21 import org
.springframework
.security
.access
.AccessDeniedException
;
22 import org
.springframework
.security
.access
.prepost
.PreAuthorize
;
23 import org
.springframework
.security
.authentication
.AuthenticationManager
;
24 import org
.springframework
.security
.authentication
.UsernamePasswordAuthenticationToken
;
25 import org
.springframework
.security
.authentication
.dao
.SaltSource
;
26 import org
.springframework
.security
.authentication
.encoding
.PasswordEncoder
;
27 import org
.springframework
.security
.core
.Authentication
;
28 import org
.springframework
.security
.core
.GrantedAuthority
;
29 import org
.springframework
.security
.core
.context
.SecurityContextHolder
;
30 import org
.springframework
.security
.core
.userdetails
.UserCache
;
31 import org
.springframework
.security
.core
.userdetails
.UserDetails
;
32 import org
.springframework
.security
.core
.userdetails
.UsernameNotFoundException
;
33 import org
.springframework
.security
.core
.userdetails
.cache
.NullUserCache
;
34 import org
.springframework
.stereotype
.Service
;
35 import org
.springframework
.transaction
.annotation
.Propagation
;
36 import org
.springframework
.transaction
.annotation
.Transactional
;
37 import org
.springframework
.util
.Assert
;
39 import eu
.etaxonomy
.cdm
.model
.common
.GrantedAuthorityImpl
;
40 import eu
.etaxonomy
.cdm
.model
.common
.Group
;
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
;
49 * Note: All group related functionality has been refactored into a GroupService. The will be removed in a future version.
52 @Transactional(propagation
= Propagation
.SUPPORTS
, readOnly
= true)
53 public class UserService
extends ServiceBase
<User
,IUserDao
> implements IUserService
{
55 protected IGroupDao groupDao
;
57 protected IGrantedAuthorityDao grantedAuthorityDao
;
59 private SaltSource saltSource
; // = new ReflectionSaltSource();
61 private PasswordEncoder passwordEncoder
; // = new Md5PasswordEncoder();
63 private AuthenticationManager authenticationManager
;
65 private UserCache userCache
= new NullUserCache();
67 @Autowired(required
= false)
68 public void setUserCache(UserCache userCache
) {
69 Assert
.notNull(userCache
, "userCache cannot be null");
70 this.userCache
= userCache
;
73 @Autowired(required
= false)
74 public void setPasswordEncoder(PasswordEncoder passwordEncoder
) {
76 this.passwordEncoder
= passwordEncoder
;
79 @Autowired(required
= false)
80 public void setSaltSource(SaltSource saltSource
) {
81 this.saltSource
= saltSource
;
84 @Autowired(required
= false)
85 public void setAuthenticationManager(AuthenticationManager authenticationManager
) {
86 this.authenticationManager
= authenticationManager
;
91 protected void setDao(IUserDao dao
) {
96 public void setGroupDao(IGroupDao groupDao
) {
97 this.groupDao
= groupDao
;
101 public void setGrantedAuthorityDao(IGrantedAuthorityDao grantedAuthorityDao
) {
102 this.grantedAuthorityDao
= grantedAuthorityDao
;
105 @Transactional(readOnly
=false)
106 protected Authentication
createNewAuthentication(Authentication currentAuth
, String newPassword
) {
107 UserDetails user
= loadUserByUsername(currentAuth
.getName());
109 UsernamePasswordAuthenticationToken newAuthentication
= new UsernamePasswordAuthenticationToken(user
, user
.getPassword(), user
.getAuthorities());
110 newAuthentication
.setDetails(currentAuth
.getDetails());
112 return newAuthentication
;
116 @Transactional(readOnly
=false)
117 public void changePassword(String oldPassword
, String newPassword
) {
118 Assert
.hasText(oldPassword
);
119 Assert
.hasText(newPassword
);
120 Authentication authentication
= SecurityContextHolder
.getContext().getAuthentication();
121 if(authentication
!= null && authentication
.getPrincipal() != null && authentication
.getPrincipal() instanceof User
) {
122 User user
= (User
)authentication
.getPrincipal();
124 authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(user
.getUsername(), oldPassword
));
126 Object salt
= this.saltSource
.getSalt(user
);
128 String password
= passwordEncoder
.encodePassword(newPassword
, salt
);
129 ((User
)user
).setPassword(password
);
131 dao
.update((User
)user
);
132 SecurityContextHolder
.getContext().setAuthentication(createNewAuthentication(authentication
, newPassword
));
133 userCache
.removeUserFromCache(user
.getUsername());
135 throw new AccessDeniedException("Can't change password as no Authentication object found in context for current user.");
140 @Transactional(readOnly
=false)
141 @PreAuthorize("#username == authentication.name or hasRole('ROLE_ADMIN')")
142 public void changePasswordForUser(String username
, String newPassword
) {
143 Assert
.hasText(username
);
144 Assert
.hasText(newPassword
);
147 User user
= dao
.findUserByUsername(username
);
149 throw new UsernameNotFoundException(username
);
152 Object salt
= this.saltSource
.getSalt(user
);
154 String password
= passwordEncoder
.encodePassword(newPassword
, salt
);
155 ((User
)user
).setPassword(password
);
157 dao
.update((User
)user
);
158 userCache
.removeUserFromCache(user
.getUsername());
159 } catch(NonUniqueResultException nure
) {
160 throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username
+ "'", 1);
165 @Transactional(readOnly
=false)
166 public void createUser(UserDetails user
) {
167 Assert
.isInstanceOf(User
.class, user
);
169 String rawPassword
= user
.getPassword();
170 Object salt
= this.saltSource
.getSalt(user
);
172 String password
= passwordEncoder
.encodePassword(rawPassword
, salt
);
173 ((User
)user
).setPassword(password
);
175 dao
.save((User
)user
);
179 @Transactional(readOnly
=false)
180 public void deleteUser(String username
) {
181 Assert
.hasLength(username
);
183 User user
= dao
.findUserByUsername(username
);
185 dao
.delete((User
)user
);
188 userCache
.removeUserFromCache(username
);
192 @Transactional(readOnly
=false)
193 public void updateUser(UserDetails user
) {
194 Assert
.isInstanceOf(User
.class, user
);
196 dao
.update((User
)user
);
197 userCache
.removeUserFromCache(user
.getUsername());
201 public boolean userExists(String username
) {
202 Assert
.hasText(username
);
204 User user
= dao
.findUserByUsername(username
);
209 * DO NOT CALL THIS METHOD IN LONG RUNNING SESSIONS OR CONVERSATIONS
210 * A THROWN UsernameNotFoundException WILL RENDER THE CONVERSATION UNUSABLE
212 public UserDetails
loadUserByUsername(String username
)
213 throws UsernameNotFoundException
, DataAccessException
{
214 Assert
.hasText(username
);
216 User user
= dao
.findUserByUsername(username
);
218 throw new UsernameNotFoundException(username
);
221 } catch(NonUniqueResultException nure
) {
222 throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username
+ "'", 1);
226 @Deprecated // use GroupService instead
227 @Transactional(readOnly
=false)
228 public void addGroupAuthority(String groupName
, GrantedAuthority authority
) {
229 Assert
.hasText(groupName
);
230 Assert
.notNull(authority
);
232 Group group
= groupDao
.findGroupByName(groupName
);
233 if(group
.getGrantedAuthorities().add(authority
)) {
234 groupDao
.update(group
);
238 @Deprecated // use GroupService instead
239 @Transactional(readOnly
=false)
240 public void addUserToGroup(String username
, String groupName
) {
241 Assert
.hasText(username
);
242 Assert
.hasText(groupName
);
244 Group group
= groupDao
.findGroupByName(groupName
);
245 User user
= dao
.findUserByUsername(username
);
247 if(group
.addMember(user
)) {
248 groupDao
.update(group
);
249 userCache
.removeUserFromCache(user
.getUsername());
253 @Deprecated // use GroupService instead
254 @Transactional(readOnly
=false)
255 public void createGroup(String groupName
, List
<GrantedAuthority
> authorities
) {
256 Assert
.hasText(groupName
);
257 Assert
.notNull(authorities
);
259 Group group
= Group
.NewInstance(groupName
);
261 for(GrantedAuthority authority
: authorities
) {
262 group
.getGrantedAuthorities().add(authority
);
265 groupDao
.save(group
);
268 @Deprecated // use GroupService instead
269 @Transactional(readOnly
=false)
270 public void deleteGroup(String groupName
) {
271 Assert
.hasText(groupName
);
273 Group group
= groupDao
.findGroupByName(groupName
);
274 groupDao
.delete(group
);
277 @Deprecated // use GroupService instead
278 public List
<String
> findAllGroups() {
279 return groupDao
.listNames(null,null);
282 @Deprecated // use GroupService instead
283 public List
<GrantedAuthority
> findGroupAuthorities(String groupName
) {
284 Assert
.hasText(groupName
);
285 Group group
= groupDao
.findGroupByName(groupName
);
287 return new ArrayList
<GrantedAuthority
>(group
.getGrantedAuthorities());
290 @Deprecated // use GroupService instead
291 public List
<String
> findUsersInGroup(String groupName
) {
292 Assert
.hasText(groupName
);
293 Group group
= groupDao
.findGroupByName(groupName
);
295 List
<String
> users
= groupDao
.listMembers(group
, null, null);
300 @Deprecated // use GroupService instead
301 @Transactional(readOnly
=false)
302 public void removeGroupAuthority(String groupName
, GrantedAuthority authority
) {
303 Assert
.hasText(groupName
);
304 Assert
.notNull(authority
);
306 Group group
= groupDao
.findGroupByName(groupName
);
308 if(group
.getGrantedAuthorities().remove(authority
)) {
309 groupDao
.update(group
);
313 @Deprecated // use GroupService instead
314 @Transactional(readOnly
=false)
315 public void removeUserFromGroup(String username
, String groupName
) {
316 Assert
.hasText(username
);
317 Assert
.hasText(groupName
);
319 Group group
= groupDao
.findGroupByName(groupName
);
320 User user
= dao
.findUserByUsername(username
);
322 if(group
.removeMember(user
)) {
323 groupDao
.update(group
);
324 userCache
.removeUserFromCache(user
.getUsername());
328 @Deprecated // use GroupService instead
329 @Transactional(readOnly
=false)
330 public void renameGroup(String oldName
, String newName
) {
331 Assert
.hasText(oldName
);
332 Assert
.hasText(newName
);
334 Group group
= groupDao
.findGroupByName(oldName
);
336 group
.setName(newName
);
337 groupDao
.update(group
);
340 @Transactional(readOnly
=false)
341 public UUID
save(User user
) {
342 if(user
.getId() == 0 || dao
.load(user
.getUuid()) == null){
347 return user
.getUuid();
351 public UUID
update(User user
) {
353 return user
.getUuid();
357 @Transactional(readOnly
=false)
358 public UUID
saveGrantedAuthority(GrantedAuthority grantedAuthority
) {
359 return grantedAuthorityDao
.save((GrantedAuthorityImpl
)grantedAuthority
);
362 @Deprecated // use GroupService instead
363 @Transactional(readOnly
=false)
364 public UUID
saveGroup(Group group
) {
365 return groupDao
.save(group
);
369 @Transactional(readOnly
= true)
370 public List
<User
> listByUsername(String queryString
,MatchMode matchmode
, List
<Criterion
> criteria
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
371 Integer numberOfResults
= dao
.countByUsername(queryString
, matchmode
, criteria
);
373 List
<User
> results
= new ArrayList
<User
>();
374 if(numberOfResults
> 0) {
375 results
= dao
.findByUsername(queryString
, matchmode
, criteria
, pageSize
, pageNumber
, orderHints
, propertyPaths
);