View Javadoc

1   package org.appfuse.service.impl;
2   
3   import org.apache.commons.lang.StringUtils;
4   import org.appfuse.dao.UserDao;
5   import org.appfuse.model.User;
6   import org.appfuse.service.MailEngine;
7   import org.appfuse.service.UserExistsException;
8   import org.appfuse.service.UserManager;
9   import org.appfuse.service.UserService;
10  import org.springframework.beans.factory.annotation.Autowired;
11  import org.springframework.beans.factory.annotation.Qualifier;
12  import org.springframework.mail.SimpleMailMessage;
13  import org.springframework.security.core.userdetails.UsernameNotFoundException;
14  import org.springframework.security.crypto.password.PasswordEncoder;
15  import org.springframework.stereotype.Service;
16  
17  import javax.jws.WebService;
18  import java.io.Serializable;
19  import java.util.HashMap;
20  import java.util.List;
21  import java.util.Map;
22  
23  
24  /**
25   * Implementation of UserManager interface.
26   *
27   * @author <a href="mailto:matt@raibledesigns.com">Matt Raible</a>
28   */
29  @Service("userManager")
30  @WebService(serviceName = "UserService", endpointInterface = "org.appfuse.service.UserService")
31  public class UserManagerImpl extends GenericManagerImpl<User, Long> implements UserManager, UserService {
32      private PasswordEncoder passwordEncoder;
33      private UserDao userDao;
34  
35      private MailEngine mailEngine;
36      private SimpleMailMessage message;
37      private PasswordTokenManager passwordTokenManager;
38  
39      private String passwordRecoveryTemplate = "passwordRecovery.vm";
40      private String passwordUpdatedTemplate = "passwordUpdated.vm";
41  
42      @Autowired
43      @Qualifier("passwordEncoder")
44      public void setPasswordEncoder(final PasswordEncoder passwordEncoder) {
45          this.passwordEncoder = passwordEncoder;
46      }
47  
48      @Override
49      @Autowired
50      public void setUserDao(final UserDao userDao) {
51          this.dao = userDao;
52          this.userDao = userDao;
53      }
54  
55      @Autowired(required = false)
56      public void setMailEngine(final MailEngine mailEngine) {
57          this.mailEngine = mailEngine;
58      }
59  
60      @Autowired(required = false)
61      public void setMailMessage(final SimpleMailMessage message) {
62          this.message = message;
63      }
64  
65      @Autowired(required = false)
66      public void setPasswordTokenManager(final PasswordTokenManager passwordTokenManager) {
67          this.passwordTokenManager = passwordTokenManager;
68      }
69  
70      /**
71       * Velocity template name to send users a password recovery mail (default
72       * passwordRecovery.vm).
73       *
74       * @param passwordRecoveryTemplate the Velocity template to use (relative to classpath)
75       * @see org.appfuse.service.MailEngine#sendMessage(org.springframework.mail.SimpleMailMessage, String, java.util.Map)
76       */
77      public void setPasswordRecoveryTemplate(final String passwordRecoveryTemplate) {
78          this.passwordRecoveryTemplate = passwordRecoveryTemplate;
79      }
80  
81      /**
82       * Velocity template name to inform users their password was updated
83       * (default passwordUpdated.vm).
84       *
85       * @param passwordUpdatedTemplate the Velocity template to use (relative to classpath)
86       * @see org.appfuse.service.MailEngine#sendMessage(org.springframework.mail.SimpleMailMessage, String, java.util.Map)
87       */
88      public void setPasswordUpdatedTemplate(final String passwordUpdatedTemplate) {
89          this.passwordUpdatedTemplate = passwordUpdatedTemplate;
90      }
91  
92      /**
93       * {@inheritDoc}
94       */
95      @Override
96      public User getUser(final String userId) {
97          return userDao.get(new Long(userId));
98      }
99  
100     /**
101      * {@inheritDoc}
102      */
103     @Override
104     public List<User> getUsers() {
105         return userDao.getAllDistinct();
106     }
107 
108     /**
109      * {@inheritDoc}
110      */
111     @Override
112     public User saveUser(final User user) throws UserExistsException {
113 
114         if (user.getVersion() == null) {
115             // if new user, lowercase userId
116             user.setUsername(user.getUsername().toLowerCase());
117         }
118 
119         // Get and prepare password management-related artifacts
120         boolean passwordChanged = false;
121         if (passwordEncoder != null) {
122             // Check whether we have to encrypt (or re-encrypt) the password
123             if (user.getVersion() == null) {
124                 // New user, always encrypt
125                 passwordChanged = true;
126             } else {
127                 // Existing user, check password in DB
128                 final String currentPassword = userDao.getUserPassword(user.getId());
129                 if (currentPassword == null) {
130                     passwordChanged = true;
131                 } else {
132                     if (!currentPassword.equals(user.getPassword())) {
133                         passwordChanged = true;
134                     }
135                 }
136             }
137 
138             // If password was changed (or new user), encrypt it
139             if (passwordChanged) {
140                 user.setPassword(passwordEncoder.encode(user.getPassword()));
141             }
142         } else {
143             log.warn("PasswordEncoder not set, skipping password encryption...");
144         }
145 
146         try {
147             return userDao.saveUser(user);
148         } catch (final Exception e) {
149             e.printStackTrace();
150             log.warn(e.getMessage());
151             throw new UserExistsException("User '" + user.getUsername() + "' already exists!");
152         }
153     }
154 
155     /**
156      * {@inheritDoc}
157      */
158     @Override
159     public void removeUser(final User user) {
160         log.debug("removing user: " + user);
161         userDao.remove(user);
162     }
163 
164     /**
165      * {@inheritDoc}
166      */
167     @Override
168     public void removeUser(final String userId) {
169         log.debug("removing user: " + userId);
170         userDao.remove(new Long(userId));
171     }
172 
173     /**
174      * {@inheritDoc}
175      *
176      * @param username the login name of the human
177      * @return User the populated user object
178      * @throws org.springframework.security.core.userdetails.UsernameNotFoundException thrown when username not found
179      */
180     @Override
181     public User getUserByUsername(final String username) throws UsernameNotFoundException {
182         return (User) userDao.loadUserByUsername(username);
183     }
184 
185     /**
186      * {@inheritDoc}
187      */
188     @Override
189     public List<User> search(final String searchTerm) {
190         return super.search(searchTerm, User.class);
191     }
192 
193     @Override
194     public String buildRecoveryPasswordUrl(final User user, final String urlTemplate) {
195         final String token = generateRecoveryToken(user);
196         final String username = user.getUsername();
197         return StringUtils.replaceEach(urlTemplate,
198                 new String[]{"{username}", "{token}"},
199                 new String[]{username, token});
200     }
201 
202     @Override
203     public String generateRecoveryToken(final User user) {
204         return passwordTokenManager.generateRecoveryToken(user);
205     }
206 
207     /**
208      * {@inheritDoc}
209      */
210     @Override
211     public boolean isRecoveryTokenValid(final String username, final String token) {
212         return isRecoveryTokenValid(getUserByUsername(username), token);
213     }
214 
215     @Override
216     public boolean isRecoveryTokenValid(final User user, final String token) {
217         return passwordTokenManager.isRecoveryTokenValid(user, token);
218     }
219 
220     /**
221      * {@inheritDoc}
222      */
223     @Override
224     public void sendPasswordRecoveryEmail(final String username, final String urlTemplate) {
225         log.debug("Sending password recovery token to user: " + username);
226 
227         final User user = getUserByUsername(username);
228         final String url = buildRecoveryPasswordUrl(user, urlTemplate);
229 
230         sendUserEmail(user, passwordRecoveryTemplate, url, "Password Recovery");
231     }
232 
233     private void sendUserEmail(final User user, final String template, final String url, final String subject) {
234         message.setTo(user.getFullName() + "<" + user.getEmail() + ">");
235         message.setSubject(subject);
236 
237         final Map<String, Serializable> model = new HashMap<String, Serializable>();
238         model.put("user", user);
239         model.put("applicationURL", url);
240 
241         mailEngine.sendMessage(message, template, model);
242     }
243 
244     /**
245      * {@inheritDoc}
246      */
247     @Override
248     public User updatePassword(final String username, final String currentPassword, final String recoveryToken, final String newPassword, final String applicationUrl) throws UserExistsException {
249         User user = getUserByUsername(username);
250         if (isRecoveryTokenValid(user, recoveryToken)) {
251             log.debug("Updating password from recovery token for user: " + username);
252             user.setPassword(newPassword);
253             user = saveUser(user);
254             passwordTokenManager.invalidateRecoveryToken(user, recoveryToken);
255 
256             sendUserEmail(user, passwordUpdatedTemplate, applicationUrl, "Password Updated");
257 
258             return user;
259         } else if (StringUtils.isNotBlank(currentPassword)) {
260             if (passwordEncoder.matches(currentPassword, user.getPassword())) {
261                 log.debug("Updating password (providing current password) for user:" + username);
262                 user.setPassword(newPassword);
263                 user = saveUser(user);
264                 return user;
265             }
266         }
267         // or throw exception
268         return null;
269     }
270 }