Project

General

Profile

Download (13 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
 * Copyright (C) 2007 EDIT
3
 * European Distributed Institute of Taxonomy
4
 * http://www.e-taxonomy.eu
5
 *
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.
8
 */
9
package eu.etaxonomy.cdm.api.service;
10

    
11
import java.util.ArrayList;
12
import java.util.Collection;
13
import java.util.HashMap;
14
import java.util.List;
15
import java.util.Map;
16
import java.util.UUID;
17

    
18
import org.hibernate.NonUniqueResultException;
19
import org.hibernate.criterion.Criterion;
20
import org.springframework.beans.factory.annotation.Autowired;
21
import org.springframework.context.annotation.Lazy;
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;
40

    
41
import eu.etaxonomy.cdm.model.permission.GrantedAuthorityImpl;
42
import eu.etaxonomy.cdm.model.permission.User;
43
import eu.etaxonomy.cdm.persistence.dao.permission.IGrantedAuthorityDao;
44
import eu.etaxonomy.cdm.persistence.dao.permission.IGroupDao;
45
import eu.etaxonomy.cdm.persistence.dao.permission.IUserDao;
46
import eu.etaxonomy.cdm.persistence.query.MatchMode;
47
import eu.etaxonomy.cdm.persistence.query.OrderHint;
48

    
49
/**
50
 * Note: All group related functionality has been refactored into a GroupService.
51
 * The will be removed in a future version.
52
 */
53
@Service
54
@Transactional(readOnly = true)
55
// NOTE: no type level @PreAuthorize annotation for this class!
56
public class UserService extends ServiceBase<User,IUserDao> implements IUserService {
57

    
58
    protected IGroupDao groupDao;
59

    
60
    protected IGrantedAuthorityDao grantedAuthorityDao;
61

    
62
    private SaltSource saltSource; // = new ReflectionSaltSource();
63

    
64
    private PasswordEncoder passwordEncoder; // = new Md5PasswordEncoder();
65

    
66
    private AuthenticationManager authenticationManager;
67

    
68

    
69
    private UserCache userCache = new NullUserCache();
70

    
71
    @Autowired(required = false)
72
    public void setUserCache(UserCache userCache) {
73
        Assert.notNull(userCache, "userCache cannot be null");
74
        this.userCache = userCache;
75
    }
76

    
77
    @Autowired(required = false)
78
    public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
79

    
80
        this.passwordEncoder = passwordEncoder;
81
    }
82

    
83
    @Autowired(required = false)
84
    public void setSaltSource(SaltSource saltSource) {
85
        this.saltSource = saltSource;
86
    }
87

    
88
    @Autowired(required= false)
89
    @Lazy // avoid dependency cycle coming from OAuth2ServerConfiguration.AuthorizationServerConfiguration.authenticationManager
90
    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
91
        this.authenticationManager = authenticationManager;
92
    }
93

    
94
    @Override
95
    @Autowired
96
    protected void setDao(IUserDao dao) {
97
        this.dao = dao;
98
    }
99

    
100
    @Autowired
101
    public void setGroupDao(IGroupDao groupDao) {
102
        this.groupDao = groupDao;
103
    }
104

    
105
    @Autowired
106
    public void setGrantedAuthorityDao(IGrantedAuthorityDao grantedAuthorityDao) {
107
        this.grantedAuthorityDao = grantedAuthorityDao;
108
    }
109

    
110

    
111
    /**
112
     * Changes the own password of in the database of the user which is
113
     * currently authenticated. Requires to supply the old password for security
114
     * reasons. Refreshes the authentication in the SecurityContext after the
115
     * password change by re-authenticating the user with the new password.
116
     *
117
     * @see org.springframework.security.provisioning.UserDetailsManager#changePassword(java.lang.String,
118
     *      java.lang.String)
119
     */
120
    @Override
121
    @Transactional(readOnly=false)
122
    @PreAuthorize("isAuthenticated()")
123
    public void changePassword(String oldPassword, String newPassword) {
124
        Assert.hasText(oldPassword);
125
        Assert.hasText(newPassword);
126
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
127
        if(authentication != null && authentication.getPrincipal() != null && authentication.getPrincipal() instanceof User) {
128

    
129
            // get current authentication and load it from the persistence layer,
130
            // to make sure we are modifying the instance which is
131
            // attached to the hibernate session
132
            User user = (User)authentication.getPrincipal();
133
            user = dao.load(user.getUuid());
134

    
135
            // check if old password is valid
136
            authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), oldPassword));
137

    
138
            // make new password and set it
139
            Object salt = this.saltSource.getSalt(user);
140
            String password = passwordEncoder.encodePassword(newPassword, salt);
141
            user.setPassword(password);
142
            dao.update(user);
143

    
144
            // authenticate the user again with the new password
145
            UsernamePasswordAuthenticationToken newAuthentication = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
146
            newAuthentication.setDetails(authentication.getDetails());
147
            SecurityContextHolder.getContext().setAuthentication(newAuthentication);
148
            userCache.removeUserFromCache(user.getUsername());
149

    
150
        } else {
151
            throw new AccessDeniedException("Can't change password as no Authentication object found in context for current user.");
152
        }
153
    }
154

    
155
    @Override
156
    @Transactional(readOnly=false)
157
    @PreAuthorize("#username == authentication.name or hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
158
    public void changePasswordForUser(String username, String newPassword) {
159
        Assert.hasText(username);
160
        Assert.hasText(newPassword);
161

    
162
        try {
163
            User user = dao.findUserByUsername(username);
164
            if(user == null) {
165
                throw new UsernameNotFoundException(username);
166
            }
167

    
168
            Object salt = this.saltSource.getSalt(user);
169

    
170
            String password = passwordEncoder.encodePassword(newPassword, salt);
171
            user.setPassword(password);
172

    
173
            dao.update(user);
174
            userCache.removeUserFromCache(user.getUsername());
175
        } catch(NonUniqueResultException nure) {
176
            throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username + "'", 1);
177
        }
178
    }
179

    
180
    @Override
181
    @Transactional(readOnly=false)
182
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
183
    public void createUser(UserDetails user) {
184
    	Assert.isInstanceOf(User.class, user);
185

    
186
        String rawPassword = user.getPassword();
187
        Object salt = this.saltSource.getSalt(user);
188

    
189
        String password = passwordEncoder.encodePassword(rawPassword, salt);
190
        ((User)user).setPassword(password);
191

    
192
        dao.save((User)user);
193
    }
194

    
195
    @Override
196
    @Transactional(readOnly=false)
197
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
198
    public void deleteUser(String username) {
199
        Assert.hasLength(username);
200

    
201
        User user = dao.findUserByUsername(username);
202
        if(user != null) {
203
            dao.delete(user);
204
        }
205

    
206
        userCache.removeUserFromCache(username);
207
    }
208

    
209
    @Override
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);
214

    
215
        dao.update((User)user);
216
        userCache.removeUserFromCache(user.getUsername());
217
    }
218

    
219
    @Override
220
    public boolean userExists(String username) {
221
        Assert.hasText(username);
222

    
223
        User user = dao.findUserByUsername(username);
224
        return user != null;
225
    }
226

    
227
    /**
228
     * <b>DO NOT CALL THIS METHOD IN LONG RUNNING SESSIONS OR CONVERSATIONS
229
     * A THROWN UsernameNotFoundException WILL RENDER THE CONVERSATION UNUSABLE</b>
230
     *
231
     * @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
232
     */
233
    // NOTE: this method must not be secured since it is being used during the
234
    //       authentication process
235
    @Override
236
    public UserDetails loadUserByUsername(String username)
237
            throws UsernameNotFoundException, DataAccessException {
238
        Assert.hasText(username);
239
        try {
240
            User user = dao.findUserByUsername(username);
241
            if(user == null) {
242
                throw new UsernameNotFoundException(username);
243
            }
244
            return user;
245
        } catch(NonUniqueResultException nure) {
246
            throw new IncorrectResultSizeDataAccessException("More than one user found with name '" + username + "'", 1);
247
        }
248
    }
249

    
250

    
251
    @Override
252
    @Transactional(readOnly=false)
253
   // @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_RUN_AS_ADMIN') or hasRole('ROLE_USER_MANAGER')")
254
    public User save(User user) {
255
        if(user.getId() == 0 || dao.load(user.getUuid()) == null){
256
            createUser(user);
257
        }else{
258
            updateUser(user);
259
        }
260
        return user;
261
    }
262

    
263
    @Override
264
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
265
    public UUID update(User user) {
266
        updateUser(user);
267
        return user.getUuid();
268
    }
269

    
270
    @Override
271
    @Transactional(readOnly=false)
272
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
273
    public UUID saveGrantedAuthority(GrantedAuthority grantedAuthority) {
274
        return grantedAuthorityDao.save((GrantedAuthorityImpl)grantedAuthority).getUuid();
275
    }
276

    
277

    
278

    
279
    @Override
280
    @Transactional(readOnly = true)
281
    public List<User> listByUsername(String queryString,MatchMode matchmode, List<Criterion> criteria, Integer pageSize, Integer pageNumber, List<OrderHint> orderHints, List<String> propertyPaths) {
282
         long numberOfResults = dao.countByUsername(queryString, matchmode, criteria);
283

    
284
         List<User> results = new ArrayList<>();
285
         if(numberOfResults > 0) {
286
                results = dao.findByUsername(queryString, matchmode, criteria, pageSize, pageNumber, orderHints, propertyPaths);
287
         }
288
         return results;
289
    }
290

    
291
    /* ================================================
292
     *  overriding methods to secure them
293
     *  via the type level annotation @PreAuthorize
294
     * ================================================ */
295

    
296
    @Override
297
    @Transactional(readOnly=false)
298
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
299
    public DeleteResult delete(User persistentObject)  {
300
        return super.delete(persistentObject);
301
    }
302

    
303
    @Override
304
    @Transactional(readOnly=false)
305
    public DeleteResult delete(UUID userUuid)  {
306
        return delete(dao.load(userUuid));
307
    }
308

    
309
    @Override
310
    @Transactional(readOnly=false)
311
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
312
    public Map<UUID, User> save(Collection<User> newInstances) {
313
        Map<UUID, User> users = new HashMap<UUID, User>();
314
    	for (User user: newInstances){
315
        	createUser(user);
316
        	users.put(user.getUuid(), user);
317
        }
318
    	return users;
319
    }
320

    
321
    @Override
322
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
323
    public UUID saveOrUpdate(User transientObject) {
324
        return super.saveOrUpdate(transientObject);
325
    }
326

    
327
    @Override
328
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
329
    @Transactional(readOnly=false)
330
    public User merge(User detachedObject) {
331
        return super.merge(detachedObject);
332
    }
333

    
334
    @Override
335
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
336
    @Transactional(readOnly=false)
337
    public List<User> merge(List<User> detachedObjects) {
338
        return super.merge(detachedObjects);
339
    }
340

    
341
    @Override
342
    @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER_MANAGER')")
343
    public Map<UUID, User> saveOrUpdate(Collection<User> transientInstances) {
344
        return super.saveOrUpdate(transientInstances);
345
    }
346

    
347
}
(98-98/100)