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
.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
.Propagation
;
38 import org
.springframework
.transaction
.annotation
.Transactional
;
39 import org
.springframework
.util
.Assert
;
41 import eu
.etaxonomy
.cdm
.model
.common
.GrantedAuthorityImpl
;
42 import eu
.etaxonomy
.cdm
.model
.common
.Group
;
43 import eu
.etaxonomy
.cdm
.model
.common
.User
;
44 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IGrantedAuthorityDao
;
45 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IGroupDao
;
46 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.IUserDao
;
47 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
48 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
51 * Note: All group related functionality has been refactored into a GroupService. The will be removed in a future version.
54 @Transactional(readOnly
= true)
55 // NOTE: no type level @PreAuthorize annotation for this class!
56 public class UserService
extends ServiceBase
<User
,IUserDao
> implements IUserService
{
58 protected IGroupDao groupDao
;
60 protected IGrantedAuthorityDao grantedAuthorityDao
;
62 private SaltSource saltSource
; // = new ReflectionSaltSource();
64 private PasswordEncoder passwordEncoder
; // = new Md5PasswordEncoder();
66 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
;
108 @Transactional(readOnly
=false)
109 protected Authentication
createNewAuthentication(Authentication currentAuth
, String newPassword
) {
110 UserDetails user
= loadUserByUsername(currentAuth
.getName());
112 UsernamePasswordAuthenticationToken newAuthentication
= new UsernamePasswordAuthenticationToken(user
, user
.getPassword(), user
.getAuthorities());
113 newAuthentication
.setDetails(currentAuth
.getDetails());
115 return newAuthentication
;
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
) {
126 User user
= (User
)authentication
.getPrincipal();
128 authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(user
.getUsername(), oldPassword
));
130 Object salt
= this.saltSource
.getSalt(user
);
132 String password
= passwordEncoder
.encodePassword(newPassword
, salt
);
133 ((User
)user
).setPassword(password
);
135 dao
.update((User
)user
);
136 SecurityContextHolder
.getContext().setAuthentication(createNewAuthentication(authentication
, newPassword
));
137 userCache
.removeUserFromCache(user
.getUsername());
139 throw new AccessDeniedException("Can't change password as no Authentication object found in context for current user.");
144 * @see eu.etaxonomy.cdm.api.service.IUserService#changePasswordForUser(java.lang.String, java.lang.String)
147 @Transactional(readOnly
=false)
148 @PreAuthorize("#username == authentication.name or hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
149 public void changePasswordForUser(String username
, String newPassword
) {
150 Assert
.hasText(username
);
151 Assert
.hasText(newPassword
);
154 User user
= dao
.findUserByUsername(username
);
156 throw new UsernameNotFoundException(username
);
159 Object salt
= this.saltSource
.getSalt(user
);
161 String password
= passwordEncoder
.encodePassword(newPassword
, salt
);
162 ((User
)user
).setPassword(password
);
164 dao
.update((User
)user
);
165 userCache
.removeUserFromCache(user
.getUsername());
166 } catch(NonUniqueResultException nure
) {
167 throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username
+ "'", 1);
172 * @see org.springframework.security.provisioning.UserDetailsManager#createUser(org.springframework.security.core.userdetails.UserDetails)
175 @Transactional(readOnly
=false)
176 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
177 public void createUser(UserDetails user
) {
178 Assert
.isInstanceOf(User
.class, user
);
180 String rawPassword
= user
.getPassword();
181 Object salt
= this.saltSource
.getSalt(user
);
183 String password
= passwordEncoder
.encodePassword(rawPassword
, salt
);
184 ((User
)user
).setPassword(password
);
186 dao
.save((User
)user
);
190 * @see org.springframework.security.provisioning.UserDetailsManager#deleteUser(java.lang.String)
193 @Transactional(readOnly
=false)
194 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
195 public void deleteUser(String username
) {
196 Assert
.hasLength(username
);
198 User user
= dao
.findUserByUsername(username
);
200 dao
.delete((User
)user
);
203 userCache
.removeUserFromCache(username
);
207 * @see org.springframework.security.provisioning.UserDetailsManager#updateUser(org.springframework.security.core.userdetails.UserDetails)
210 @Transactional(readOnly
=false)
211 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
212 public void updateUser(UserDetails user
) {
213 Assert
.isInstanceOf(User
.class, user
);
215 dao
.update((User
)user
);
216 userCache
.removeUserFromCache(user
.getUsername());
220 * @see org.springframework.security.provisioning.UserDetailsManager#userExists(java.lang.String)
223 public boolean userExists(String username
) {
224 Assert
.hasText(username
);
226 User user
= dao
.findUserByUsername(username
);
231 * <b>DO NOT CALL THIS METHOD IN LONG RUNNING SESSIONS OR CONVERSATIONS
232 * A THROWN UsernameNotFoundException WILL RENDER THE CONVERSATION UNUSABLE</b>
234 * @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
236 // NOTE: this method must not be secured since it is being used during the
237 // authentication process
238 public UserDetails
loadUserByUsername(String username
)
239 throws UsernameNotFoundException
, DataAccessException
{
240 Assert
.hasText(username
);
242 User user
= dao
.findUserByUsername(username
);
244 throw new UsernameNotFoundException(username
);
247 } catch(NonUniqueResultException nure
) {
248 throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username
+ "'", 1);
252 @Deprecated // use GroupService instead
253 @Transactional(readOnly
=false)
254 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
255 public void addGroupAuthority(String groupName
, GrantedAuthority authority
) {
256 Assert
.hasText(groupName
);
257 Assert
.notNull(authority
);
259 Group group
= groupDao
.findGroupByName(groupName
);
260 if(group
.getGrantedAuthorities().add(authority
)) {
261 groupDao
.update(group
);
265 @Deprecated // use GroupService instead
266 @Transactional(readOnly
=false)
267 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
268 public void addUserToGroup(String username
, String groupName
) {
269 Assert
.hasText(username
);
270 Assert
.hasText(groupName
);
272 Group group
= groupDao
.findGroupByName(groupName
);
273 User user
= dao
.findUserByUsername(username
);
275 if(group
.addMember(user
)) {
276 groupDao
.update(group
);
277 userCache
.removeUserFromCache(user
.getUsername());
281 @Deprecated // use GroupService instead
282 @Transactional(readOnly
=false)
283 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
284 public void createGroup(String groupName
, List
<GrantedAuthority
> authorities
) {
285 Assert
.hasText(groupName
);
286 Assert
.notNull(authorities
);
288 Group group
= Group
.NewInstance(groupName
);
290 for(GrantedAuthority authority
: authorities
) {
291 group
.getGrantedAuthorities().add(authority
);
294 groupDao
.save(group
);
297 @Deprecated // use GroupService instead
298 @Transactional(readOnly
=false)
299 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
300 public void deleteGroup(String groupName
) {
301 Assert
.hasText(groupName
);
303 Group group
= groupDao
.findGroupByName(groupName
);
304 groupDao
.delete(group
);
307 @Deprecated // use GroupService instead
308 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
309 public List
<String
> findAllGroups() {
310 return groupDao
.listNames(null,null);
313 @Deprecated // use GroupService instead
314 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
315 public List
<GrantedAuthority
> findGroupAuthorities(String groupName
) {
316 Assert
.hasText(groupName
);
317 Group group
= groupDao
.findGroupByName(groupName
);
319 return new ArrayList
<GrantedAuthority
>(group
.getGrantedAuthorities());
322 @Deprecated // use GroupService instead
323 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
324 public List
<String
> findUsersInGroup(String groupName
) {
325 Assert
.hasText(groupName
);
326 Group group
= groupDao
.findGroupByName(groupName
);
328 List
<String
> users
= groupDao
.listMembers(group
, null, null);
333 @Deprecated // use GroupService instead
334 @Transactional(readOnly
=false)
335 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
336 public void removeGroupAuthority(String groupName
, GrantedAuthority authority
) {
337 Assert
.hasText(groupName
);
338 Assert
.notNull(authority
);
340 Group group
= groupDao
.findGroupByName(groupName
);
342 if(group
.getGrantedAuthorities().remove(authority
)) {
343 groupDao
.update(group
);
347 @Deprecated // use GroupService instead
348 @Transactional(readOnly
=false)
349 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
350 public void removeUserFromGroup(String username
, String groupName
) {
351 Assert
.hasText(username
);
352 Assert
.hasText(groupName
);
354 Group group
= groupDao
.findGroupByName(groupName
);
355 User user
= dao
.findUserByUsername(username
);
357 if(group
.removeMember(user
)) {
358 groupDao
.update(group
);
359 userCache
.removeUserFromCache(user
.getUsername());
363 @Deprecated // use GroupService instead
364 @Transactional(readOnly
=false)
365 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
366 public void renameGroup(String oldName
, String newName
) {
367 Assert
.hasText(oldName
);
368 Assert
.hasText(newName
);
370 Group group
= groupDao
.findGroupByName(oldName
);
372 group
.setName(newName
);
373 groupDao
.update(group
);
376 @Transactional(readOnly
=false)
377 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_RUN_AS_ADMIN') or hasRole('ROLE_USER_MANAGER')")
378 public UUID
save(User user
) {
379 if(user
.getId() == 0 || dao
.load(user
.getUuid()) == null){
384 return user
.getUuid();
388 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
389 public UUID
update(User user
) {
391 return user
.getUuid();
395 @Transactional(readOnly
=false)
396 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
397 public UUID
saveGrantedAuthority(GrantedAuthority grantedAuthority
) {
398 return grantedAuthorityDao
.save((GrantedAuthorityImpl
)grantedAuthority
);
401 @Deprecated // use GroupService instead
402 @Transactional(readOnly
=false)
403 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
404 public UUID
saveGroup(Group group
) {
405 return groupDao
.save(group
);
409 * @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)
412 @Transactional(readOnly
= true)
413 public List
<User
> listByUsername(String queryString
,MatchMode matchmode
, List
<Criterion
> criteria
, Integer pageSize
, Integer pageNumber
, List
<OrderHint
> orderHints
, List
<String
> propertyPaths
) {
414 Integer numberOfResults
= dao
.countByUsername(queryString
, matchmode
, criteria
);
416 List
<User
> results
= new ArrayList
<User
>();
417 if(numberOfResults
> 0) {
418 results
= dao
.findByUsername(queryString
, matchmode
, criteria
, pageSize
, pageNumber
, orderHints
, propertyPaths
);
423 /* ================================================
424 * overriding methods to secure them
425 * via the type level annotation @PreAuthorize
426 * ================================================ */
429 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
430 public UUID
delete(User persistentObject
) {
431 return super.delete(persistentObject
);
435 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
436 public Map
<UUID
, User
> save(Collection
<User
> newInstances
) {
437 return super.save(newInstances
);
441 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
442 public UUID
saveOrUpdate(User transientObject
) {
443 return super.saveOrUpdate(transientObject
);
447 @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
448 public Map
<UUID
, User
> saveOrUpdate(Collection
<User
> transientInstances
) {
449 return super.saveOrUpdate(transientInstances
);