View Javadoc

1   /**
2    * 
3    */
4   package org.appfuse.service.impl;
5   
6   import java.util.Date;
7   
8   import javax.sql.DataSource;
9   
10  import org.apache.commons.lang.RandomStringUtils;
11  import org.apache.commons.lang.math.RandomUtils;
12  import org.apache.commons.lang.time.DateUtils;
13  import org.appfuse.model.User;
14  import org.springframework.beans.factory.annotation.Autowired;
15  import org.springframework.jdbc.core.JdbcTemplate;
16  
17  /**
18   * Provides {@link PasswordTokenManager} functionality generating and persisting
19   * random tokens to the db as an extra security check.
20   * 
21   * You will need to create a db table with the following structure:
22   * 
23   * <pre>
24   * <code>
25   * create table password_reset_token (
26   *     username varchar(50) NOT NULL,
27   *     token varchar(255) NOT NULL,
28   *     expiration_time timestamp NOT NULL,
29   *     PRIMARY KEY (username, token)
30   * )
31   * </code>
32   * </pre>
33   * 
34   * and configure this alternative PasswordTokenManager in the spring
35   * BeanFactory.
36   * 
37   * @author ivangsa
38   */
39  public class PersistentPasswordTokenManagerImpl implements PasswordTokenManager {
40  
41      private JdbcTemplate jdbcTemplate;
42  
43      private String deleteTokenSql = "delete from password_reset_token where username=?";
44      private String insertTokenSql = "insert into password_reset_token (username, token, expiration_time) values (?, ?, ?)";
45      private String selectTokenSql = "select count(token) from password_reset_token where username=? and token=? and expiration_time > NOW()";
46  
47      @Autowired
48      public void setDataSource(DataSource dataSource) {
49          jdbcTemplate = new JdbcTemplate(dataSource);
50      }
51  
52      public void setDeleteTokenSql(String deleteTokenSql) {
53          this.deleteTokenSql = deleteTokenSql;
54      }
55  
56      public void setInsertTokenSql(String insertTokenSql) {
57          this.insertTokenSql = insertTokenSql;
58      }
59  
60      public void setSelectTokenSql(String selectTokenSql) {
61          this.selectTokenSql = selectTokenSql;
62      }
63  
64      /**
65       * @see org.appfuse.service.impl.PasswordTokenManager#generateRecoveryToken(org.appfuse.model.User)
66       */
67      @Override
68      public String generateRecoveryToken(final User user) {
69  	int length = RandomUtils.nextInt(16) + 16;
70  	String token = RandomStringUtils.randomAlphanumeric(length);
71          persistToken(user, token);
72          return token;
73      }
74  
75      /**
76       * @see org.appfuse.service.impl.PasswordTokenManager#isRecoveryTokenValid(org.appfuse.model.User, java.lang.String)
77       */
78      @Override
79      public boolean isRecoveryTokenValid(final User user, final String token) {
80          return isRecoveryTokenPersisted(user, token);
81      }
82  
83      /**
84       * 
85       * @see org.appfuse.service.impl.PasswordTokenManager#invalidateRecoveryToken(User, String)
86       */
87      @Override
88      public void invalidateRecoveryToken(User user, String token) {
89          jdbcTemplate.update(deleteTokenSql, user.getUsername());
90      }
91  
92      protected void persistToken(User user, String token) {
93          jdbcTemplate.update(deleteTokenSql, user.getUsername());
94          jdbcTemplate.update(insertTokenSql, user.getUsername(), token, getExpirationTime());
95      }
96  
97      protected boolean isRecoveryTokenPersisted(final User user, final String token) {
98          Number count = jdbcTemplate.queryForObject(
99                  selectTokenSql,
100                 new Object[] { user.getUsername(), token }, Integer.class);
101         return count != null && count.intValue() == 1;
102     }
103 
104     /**
105      * Return tokens expiration time, now + 1 day.
106      * 
107      * @return
108      */
109     private Date getExpirationTime() {
110         return DateUtils.addDays(new Date(), 1);
111     }
112 }