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
.Collection
;
14 import java
.util
.HashMap
;
15 import java
.util
.List
;
17 import java
.util
.UUID
;
19 import org
.hibernate
.NonUniqueResultException
;
20 import org
.hibernate
.criterion
.Criterion
;
21 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
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
;
41 import eu
.etaxonomy
.cdm
.api
.service
.exception
.ReferencedObjectUndeletableException
;
42 import eu
.etaxonomy
.cdm
.model
.common
.GrantedAuthorityImpl
;
43 import eu
.etaxonomy
.cdm
.model
.common
.Group
;
44 import eu
.etaxonomy
.cdm
.model
.common
.User
;
45 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IGrantedAuthorityDao
;
46 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IGroupDao
;
47 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IUserDao
;
48 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
49 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
52 * Note: All group related functionality has been refactored into a GroupService. The will be removed in a future version.
55 @Transactional(readOnly
= true)
56 // NOTE: no type level @PreAuthorize annotation for this class!
57 public class UserService
extends ServiceBase
<User
,IUserDao
> implements IUserService
{
59 protected IGroupDao groupDao
;
61 protected IGrantedAuthorityDao grantedAuthorityDao
;
63 private SaltSource saltSource
; // = new ReflectionSaltSource();
65 private PasswordEncoder passwordEncoder
; // = new Md5PasswordEncoder();
67 private AuthenticationManager authenticationManager
;
69 private UserCache userCache
= new NullUserCache();
71 @Autowired(required
= false)
72 public void setUserCache(UserCache userCache
) {
73 Assert
.notNull(userCache
, "userCache cannot be null");
74 this.userCache
= userCache
;
77 @Autowired(required
= false)
78 public void setPasswordEncoder(PasswordEncoder passwordEncoder
) {
80 this.passwordEncoder
= passwordEncoder
;
83 @Autowired(required
= false)
84 public void setSaltSource(SaltSource saltSource
) {
85 this.saltSource
= saltSource
;
88 @Autowired(required
= false)
89 public void setAuthenticationManager(AuthenticationManager authenticationManager
) {
90 this.authenticationManager
= authenticationManager
;
95 protected void setDao(IUserDao dao
) {
100 public void setGroupDao(IGroupDao groupDao
) {
101 this.groupDao
= groupDao
;
105 public void setGrantedAuthorityDao(IGrantedAuthorityDao grantedAuthorityDao
) {
106 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 * @see eu.etaxonomy.cdm.api.service.IUserService#changePasswordForUser(java.lang.String, java.lang.String)
157 @Transactional(readOnly
=false)
158 @PreAuthorize("#username == authentication.name or hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
159 public void changePasswordForUser(String username
, String newPassword
) {
160 Assert
.hasText(username
);
161 Assert
.hasText(newPassword
);
164 User user
= dao
.findUserByUsername(username
);
166 throw new UsernameNotFoundException(username
);
169 Object salt
= this.saltSource
.getSalt(user
);
171 String password
= passwordEncoder
.encodePassword(newPassword
, salt
);
172 user
.setPassword(password
);
175 userCache
.removeUserFromCache(user
.getUsername());
176 } catch(NonUniqueResultException nure
) {
177 throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username
+ "'", 1);
182 * @see org.springframework.security.provisioning.UserDetailsManager#createUser(org.springframework.security.core.userdetails.UserDetails)
185 @Transactional(readOnly
=false)
186 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
187 public void createUser(UserDetails user
) {
188 Assert
.isInstanceOf(User
.class, user
);
190 String rawPassword
= user
.getPassword();
191 Object salt
= this.saltSource
.getSalt(user
);
193 String password
= passwordEncoder
.encodePassword(rawPassword
, salt
);
194 ((User
)user
).setPassword(password
);
196 UUID userUUID
= dao
.save((User
)user
);
204 * @see org.springframework.security.provisioning.UserDetailsManager#deleteUser(java.lang.String)
207 @Transactional(readOnly
=false)
208 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
209 public void deleteUser(String username
) {
210 Assert
.hasLength(username
);
212 User user
= dao
.findUserByUsername(username
);
217 userCache
.removeUserFromCache(username
);
221 * @see org.springframework.security.provisioning.UserDetailsManager#updateUser(org.springframework.security.core.userdetails.UserDetails)
224 @Transactional(readOnly
=false)
225 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
226 public void updateUser(UserDetails user
) {
227 Assert
.isInstanceOf(User
.class, user
);
229 dao
.update((User
)user
);
230 userCache
.removeUserFromCache(user
.getUsername());
234 * @see org.springframework.security.provisioning.UserDetailsManager#userExists(java.lang.String)
237 public boolean userExists(String username
) {
238 Assert
.hasText(username
);
240 User user
= dao
.findUserByUsername(username
);
245 * <b>DO NOT CALL THIS METHOD IN LONG RUNNING SESSIONS OR CONVERSATIONS
246 * A THROWN UsernameNotFoundException WILL RENDER THE CONVERSATION UNUSABLE</b>
248 * @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
250 // NOTE: this method must not be secured since it is being used during the
251 // authentication process
253 public UserDetails
loadUserByUsername(String username
)
254 throws UsernameNotFoundException
, DataAccessException
{
255 Assert
.hasText(username
);
257 User user
= dao
.findUserByUsername(username
);
259 throw new UsernameNotFoundException(username
);
262 } catch(NonUniqueResultException nure
) {
263 throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username
+ "'", 1);
268 * @see org.springframework.security.provisioning.GroupManager#addGroupAuthority(java.lang.String, org.springframework.security.core.GrantedAuthority)
271 @Deprecated // use GroupService instead
272 @Transactional(readOnly
=false)
273 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
274 public void addGroupAuthority(String groupName
, GrantedAuthority authority
) {
275 Assert
.hasText(groupName
);
276 Assert
.notNull(authority
);
278 Group group
= groupDao
.findGroupByName(groupName
);
279 if(group
.getGrantedAuthorities().add(authority
)) {
280 groupDao
.update(group
);
285 * @see org.springframework.security.provisioning.GroupManager#addUserToGroup(java.lang.String, java.lang.String)
288 @Deprecated // use GroupService instead
289 @Transactional(readOnly
=false)
290 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
291 public void addUserToGroup(String username
, String groupName
) {
292 Assert
.hasText(username
);
293 Assert
.hasText(groupName
);
295 Group group
= groupDao
.findGroupByName(groupName
);
296 User user
= dao
.findUserByUsername(username
);
298 if(group
.addMember(user
)) {
299 groupDao
.update(group
);
300 userCache
.removeUserFromCache(user
.getUsername());
305 * @see org.springframework.security.provisioning.GroupManager#createGroup(java.lang.String, java.util.List)
308 @Deprecated // use GroupService instead
309 @Transactional(readOnly
=false)
310 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
311 public void createGroup(String groupName
, List
<GrantedAuthority
> authorities
) {
312 Assert
.hasText(groupName
);
313 Assert
.notNull(authorities
);
315 Group group
= Group
.NewInstance(groupName
);
317 for(GrantedAuthority authority
: authorities
) {
318 group
.getGrantedAuthorities().add(authority
);
321 groupDao
.save(group
);
325 * @see org.springframework.security.provisioning.GroupManager#deleteGroup(java.lang.String)
328 @Deprecated // use GroupService instead
329 @Transactional(readOnly
=false)
330 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
331 public void deleteGroup(String groupName
) {
332 Assert
.hasText(groupName
);
334 Group group
= groupDao
.findGroupByName(groupName
);
335 groupDao
.delete(group
);
339 * @see org.springframework.security.provisioning.GroupManager#findAllGroups()
342 @Deprecated // use GroupService instead
343 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
344 public List
<String
> findAllGroups() {
345 return groupDao
.listNames(null,null);
349 * @see org.springframework.security.provisioning.GroupManager#findGroupAuthorities(java.lang.String)
352 @Deprecated // use GroupService instead
353 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
354 public List
<GrantedAuthority
> findGroupAuthorities(String groupName
) {
355 Assert
.hasText(groupName
);
356 Group group
= groupDao
.findGroupByName(groupName
);
358 return new ArrayList
<GrantedAuthority
>(group
.getGrantedAuthorities());
362 * @see org.springframework.security.provisioning.GroupManager#findUsersInGroup(java.lang.String)
365 @Deprecated // use GroupService instead
366 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
367 public List
<String
> findUsersInGroup(String groupName
) {
368 Assert
.hasText(groupName
);
369 Group group
= groupDao
.findGroupByName(groupName
);
371 List
<String
> users
= groupDao
.listMembers(group
, null, null);
377 * @see org.springframework.security.provisioning.GroupManager#removeGroupAuthority(java.lang.String, org.springframework.security.core.GrantedAuthority)
380 @Deprecated // use GroupService instead
381 @Transactional(readOnly
=false)
382 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
383 public void removeGroupAuthority(String groupName
, GrantedAuthority authority
) {
384 Assert
.hasText(groupName
);
385 Assert
.notNull(authority
);
387 Group group
= groupDao
.findGroupByName(groupName
);
389 if(group
.getGrantedAuthorities().remove(authority
)) {
390 groupDao
.update(group
);
395 * @see org.springframework.security.provisioning.GroupManager#removeUserFromGroup(java.lang.String, java.lang.String)
398 @Deprecated // use GroupService instead
399 @Transactional(readOnly
=false)
400 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
401 public void removeUserFromGroup(String username
, String groupName
) {
402 Assert
.hasText(username
);
403 Assert
.hasText(groupName
);
405 Group group
= groupDao
.findGroupByName(groupName
);
406 User user
= dao
.findUserByUsername(username
);
408 if(group
.removeMember(user
)) {
409 groupDao
.update(group
);
410 userCache
.removeUserFromCache(user
.getUsername());
415 * @see org.springframework.security.provisioning.GroupManager#renameGroup(java.lang.String, java.lang.String)
418 @Deprecated // use GroupService instead
419 @Transactional(readOnly
=false)
420 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
421 public void renameGroup(String oldName
, String newName
) {
422 Assert
.hasText(oldName
);
423 Assert
.hasText(newName
);
425 Group group
= groupDao
.findGroupByName(oldName
);
427 group
.setName(newName
);
428 groupDao
.update(group
);
432 * @see eu.etaxonomy.cdm.api.service.ServiceBase#save(eu.etaxonomy.cdm.model.common.CdmBase)
435 @Transactional(readOnly
=false)
436 // @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_RUN_AS_ADMIN') or hasRole('ROLE_USER_MANAGER')")
437 public UUID
save(User user
) {
438 if(user
.getId() == 0 || dao
.load(user
.getUuid()) == null){
443 return user
.getUuid();
447 * @see eu.etaxonomy.cdm.api.service.ServiceBase#update(eu.etaxonomy.cdm.model.common.CdmBase)
450 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
451 public UUID
update(User user
) {
453 return user
.getUuid();
457 * @see eu.etaxonomy.cdm.api.service.IUserService#saveGrantedAuthority(org.springframework.security.core.GrantedAuthority)
460 @Transactional(readOnly
=false)
461 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
462 public UUID
saveGrantedAuthority(GrantedAuthority grantedAuthority
) {
463 return grantedAuthorityDao
.save((GrantedAuthorityImpl
)grantedAuthority
);
469 * @see eu.etaxonomy.cdm.api.service.IUserService#listByUsername(java.lang.String, eu.etaxonomy.cdm.persistence.query.MatchMode, java.util.List, java.lang.Integer, java.lang.Integer, java.util.List, java.util.List)
472 @Transactional(readOnly
= true)
473 public List
<User
> listByUsername(String queryString
,MatchMode matchmode
, List
<Criterion
> criteria
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
474 Integer numberOfResults
= dao
.countByUsername(queryString
, matchmode
, criteria
);
476 List
<User
> results
= new ArrayList
<User
>();
477 if(numberOfResults
> 0) {
478 results
= dao
.findByUsername(queryString
, matchmode
, criteria
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
483 /* ================================================
484 * overriding methods to secure them
485 * via the type level annotation @PreAuthorize
486 * ================================================ */
489 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
490 public UUID
delete(User persistentObject
) throws ReferencedObjectUndeletableException
{
491 return super.delete(persistentObject
);
495 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
496 public Map
<UUID
, User
> save(Collection
<User
> newInstances
) {
497 Map
<UUID
, User
> users
= new HashMap
<UUID
, User
>();
498 for (User user
: newInstances
){
500 users
.put(user
.getUuid(), user
);
506 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
507 public UUID
saveOrUpdate(User transientObject
) {
508 return super.saveOrUpdate(transientObject
);
512 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
513 public Map
<UUID
, User
> saveOrUpdate(Collection
<User
> transientInstances
) {
514 return super.saveOrUpdate(transientInstances
);