/** * */ package org.appfuse.service.impl; import java.util.Date; import javax.sql.DataSource; import org.apache.commons.lang.RandomStringUtils; import org.apache.commons.lang.math.RandomUtils; import org.apache.commons.lang.time.DateUtils; import org.appfuse.model.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; /** * Provides {@link PasswordTokenManager} functionality generating and persisting * random tokens to the db as an extra security check. * * You will need to create a db table with the following structure: * * <pre> * <code> * create table password_reset_token ( * username varchar(50) NOT NULL, * token varchar(255) NOT NULL, * expiration_time timestamp NOT NULL, * PRIMARY KEY (username, token) * ) * </code> * </pre> * * and configure this alternative PasswordTokenManager in the spring * BeanFactory. * * @author ivangsa */ public class PersistentPasswordTokenManagerImpl implements PasswordTokenManager { private JdbcTemplate jdbcTemplate; private String deleteTokenSql = "delete from password_reset_token where username=?"; private String insertTokenSql = "insert into password_reset_token (username, token, expiration_time) values (?, ?, ?)"; private String selectTokenSql = "select count(token) from password_reset_token where username=? and token=? and expiration_time > NOW()"; @Autowired public void setDataSource(DataSource dataSource) { jdbcTemplate = new JdbcTemplate(dataSource); } public void setDeleteTokenSql(String deleteTokenSql) { this.deleteTokenSql = deleteTokenSql; } public void setInsertTokenSql(String insertTokenSql) { this.insertTokenSql = insertTokenSql; } public void setSelectTokenSql(String selectTokenSql) { this.selectTokenSql = selectTokenSql; } /** * @see org.appfuse.service.impl.PasswordTokenManager#generateRecoveryToken(org.appfuse.model.User) */ @Override public String generateRecoveryToken(final User user) { int length = RandomUtils.nextInt(16) + 16; String token = RandomStringUtils.randomAlphanumeric(length); persistToken(user, token); return token; } /** * @see org.appfuse.service.impl.PasswordTokenManager#isRecoveryTokenValid(org.appfuse.model.User, java.lang.String) */ @Override public boolean isRecoveryTokenValid(final User user, final String token) { return isRecoveryTokenPersisted(user, token); } /** * * @see org.appfuse.service.impl.PasswordTokenManager#invalidateRecoveryToken(User, String) */ @Override public void invalidateRecoveryToken(User user, String token) { jdbcTemplate.update(deleteTokenSql, user.getUsername()); } protected void persistToken(User user, String token) { jdbcTemplate.update(deleteTokenSql, user.getUsername()); jdbcTemplate.update(insertTokenSql, user.getUsername(), token, getExpirationTime()); } protected boolean isRecoveryTokenPersisted(final User user, final String token) { Number count = jdbcTemplate.queryForObject( selectTokenSql, new Object[] { user.getUsername(), token }, Integer.class); return count != null && count.intValue() == 1; } /** * Return tokens expiration time, now + 1 day. * * @return */ private Date getExpirationTime() { return DateUtils.addDays(new Date(), 1); } }