2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
9 package eu
.etaxonomy
.cdm
.api
.service
;
11 import java
.util
.ArrayList
;
12 import java
.util
.Collection
;
13 import java
.util
.HashMap
;
14 import java
.util
.List
;
16 import java
.util
.UUID
;
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
;
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
;
49 * Note: All group related functionality has been refactored into a GroupService.
50 * The will be removed in a future version.
53 @Transactional(readOnly
= true)
54 // NOTE: no type level @PreAuthorize annotation for this class!
55 public class UserService
extends ServiceBase
<User
,IUserDao
> implements IUserService
{
57 protected IGroupDao groupDao
;
59 protected IGrantedAuthorityDao grantedAuthorityDao
;
61 private SaltSource saltSource
; // = new ReflectionSaltSource();
63 private PasswordEncoder passwordEncoder
; // = new Md5PasswordEncoder();
65 private AuthenticationManager authenticationManager
;
68 private UserCache userCache
= new NullUserCache();
70 @Autowired(required
= false)
71 public void setUserCache(UserCache userCache
) {
72 Assert
.notNull(userCache
, "userCache cannot be null");
73 this.userCache
= userCache
;
76 @Autowired(required
= false)
77 public void setPasswordEncoder(PasswordEncoder passwordEncoder
) {
79 this.passwordEncoder
= passwordEncoder
;
82 @Autowired(required
= false)
83 public void setSaltSource(SaltSource saltSource
) {
84 this.saltSource
= saltSource
;
87 @Autowired(required
= false)
88 public void setAuthenticationManager(AuthenticationManager authenticationManager
) {
89 this.authenticationManager
= authenticationManager
;
94 protected void setDao(IUserDao dao
) {
99 public void setGroupDao(IGroupDao groupDao
) {
100 this.groupDao
= groupDao
;
104 public void setGrantedAuthorityDao(IGrantedAuthorityDao grantedAuthorityDao
) {
105 this.grantedAuthorityDao
= grantedAuthorityDao
;
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.
115 * @see org.springframework.security.provisioning.UserDetailsManager#changePassword(java.lang.String,
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
) {
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());
133 // check if old password is valid
134 authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(user
.getUsername(), oldPassword
));
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
);
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());
149 throw new AccessDeniedException("Can't change password as no Authentication object found in context for current user.");
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
);
161 User user
= dao
.findUserByUsername(username
);
163 throw new UsernameNotFoundException(username
);
166 Object salt
= this.saltSource
.getSalt(user
);
168 String password
= passwordEncoder
.encodePassword(newPassword
, salt
);
169 user
.setPassword(password
);
172 userCache
.removeUserFromCache(user
.getUsername());
173 } catch(NonUniqueResultException nure
) {
174 throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username
+ "'", 1);
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
);
184 String rawPassword
= user
.getPassword();
185 Object salt
= this.saltSource
.getSalt(user
);
187 String password
= passwordEncoder
.encodePassword(rawPassword
, salt
);
188 ((User
)user
).setPassword(password
);
190 dao
.save((User
)user
);
194 @Transactional(readOnly
=false)
195 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
196 public void deleteUser(String username
) {
197 Assert
.hasLength(username
);
199 User user
= dao
.findUserByUsername(username
);
204 userCache
.removeUserFromCache(username
);
208 * @see org.springframework.security.provisioning.UserDetailsManager#updateUser(org.springframework.security.core.userdetails.UserDetails)
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
);
216 dao
.update((User
)user
);
217 userCache
.removeUserFromCache(user
.getUsername());
221 * @see org.springframework.security.provisioning.UserDetailsManager#userExists(java.lang.String)
224 public boolean userExists(String username
) {
225 Assert
.hasText(username
);
227 User user
= dao
.findUserByUsername(username
);
232 * <b>DO NOT CALL THIS METHOD IN LONG RUNNING SESSIONS OR CONVERSATIONS
233 * A THROWN UsernameNotFoundException WILL RENDER THE CONVERSATION UNUSABLE</b>
235 * @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
237 // NOTE: this method must not be secured since it is being used during the
238 // authentication process
240 public UserDetails
loadUserByUsername(String username
)
241 throws UsernameNotFoundException
, DataAccessException
{
242 Assert
.hasText(username
);
244 User user
= dao
.findUserByUsername(username
);
246 throw new UsernameNotFoundException(username
);
249 } catch(NonUniqueResultException nure
) {
250 throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username
+ "'", 1);
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){
268 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
269 public UUID
update(User user
) {
271 return user
.getUuid();
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();
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
);
288 List
<User
> results
= new ArrayList
<>();
289 if(numberOfResults
> 0) {
290 results
= dao
.findByUsername(queryString
, matchmode
, criteria
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
295 /* ================================================
296 * overriding methods to secure them
297 * via the type level annotation @PreAuthorize
298 * ================================================ */
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
);
308 @Transactional(readOnly
=false)
309 public DeleteResult
delete(UUID userUuid
) {
310 return delete(dao
.load(userUuid
));
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
){
320 users
.put(user
.getUuid(), user
);
326 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
327 public UUID
saveOrUpdate(User transientObject
) {
328 return super.saveOrUpdate(transientObject
);
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
);
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
);
346 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
347 public Map
<UUID
, User
> saveOrUpdate(Collection
<User
> transientInstances
) {
348 return super.saveOrUpdate(transientInstances
);