You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rave.apache.org by mf...@apache.org on 2012/02/22 17:05:58 UTC
svn commit: r1292365 [1/2] - in /incubator/rave/trunk: ./
rave-components/rave-commons/src/main/java/org/apache/rave/exception/
rave-components/rave-core/
rave-components/rave-core/src/main/java/org/apache/rave/portal/model/
rave-components/rave-core/s...
Author: mfranklin
Date: Wed Feb 22 16:05:57 2012
New Revision: 1292365
URL: http://svn.apache.org/viewvc?rev=1292365&view=rev
Log:
Applied patch submitted for RAVE-147
Added:
incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/exception/EmailException.java
incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/EmailService.java
incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultEmailService.java
incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/ChangePasswordController.java
incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/ReminderController.java
incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/ChangePasswordValidator.java
incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/NewPasswordValidator.java
incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/ChangePasswordControllerTest.java
incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/ReminderControllerTest.java
incubator/rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/jsp/views/changepassword.jsp
incubator/rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/jsp/views/newpassword.jsp
incubator/rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/jsp/views/retrieveusername.jsp
incubator/rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/mailtemplates/
incubator/rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/mailtemplates/password_reminder.ftl
incubator/rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/mailtemplates/username_reminder.ftl
Modified:
incubator/rave/trunk/pom.xml
incubator/rave/trunk/rave-components/rave-core/pom.xml
incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/NewUser.java
incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/User.java
incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/repository/UserRepository.java
incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/repository/impl/JpaUserRepository.java
incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/UserService.java
incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultUserService.java
incubator/rave/trunk/rave-components/rave-core/src/main/resources/org/apache/rave/core-applicationContext.xml
incubator/rave/trunk/rave-components/rave-core/src/test/resources/portal.properties
incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/NewAccountController.java
incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/util/ModelKeys.java
incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/util/ViewNames.java
incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/NewAccountValidator.java
incubator/rave/trunk/rave-portal-resources/src/main/resources/messages.properties
incubator/rave/trunk/rave-portal-resources/src/main/resources/messages_nl.properties
incubator/rave/trunk/rave-portal-resources/src/main/resources/portal.properties
incubator/rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/applicationContext-security.xml
incubator/rave/trunk/rave-portal-resources/src/main/webapp/login.jsp
incubator/rave/trunk/rave-portal/pom.xml
incubator/rave/trunk/rave-portal/src/main/dist/NOTICE
incubator/rave/trunk/rave-portal/src/test/resources/portal.properties
Modified: incubator/rave/trunk/pom.xml
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/pom.xml?rev=1292365&r1=1292364&r2=1292365&view=diff
==============================================================================
--- incubator/rave/trunk/pom.xml (original)
+++ incubator/rave/trunk/pom.xml Wed Feb 22 16:05:57 2012
@@ -71,7 +71,9 @@
<tiles.version>2.2.2</tiles.version>
<recaptcha4j.version>0.0.7</recaptcha4j.version>
<apacheds.version>1.5.5</apacheds.version>
-
+ <javax.mail.version>1.4.4</javax.mail.version>
+ <javax.activation.version>1.1</javax.activation.version>
+ <freemarker.version>2.3.18</freemarker.version>
<!-- The location of Rave's H2 file DB. No trailing / -->
<rave.database.location>/tmp/rave_db</rave.database.location>
@@ -183,6 +185,11 @@
<version>${org.springframework.version}</version>
</dependency>
<dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context-support</artifactId>
+ <version>${org.springframework.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${org.springframework.version}</version>
@@ -397,6 +404,24 @@
<artifactId>recaptcha4j</artifactId>
<version>${recaptcha4j.version}</version>
</dependency>
+ <!-- Mail-->
+ <dependency>
+ <groupId>javax.mail</groupId>
+ <artifactId>mail</artifactId>
+ <version>${javax.mail.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.activation</groupId>
+ <artifactId>activation</artifactId>
+ <version>${javax.activation.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.freemarker</groupId>
+ <artifactId>freemarker</artifactId>
+ <version>${freemarker.version}</version>
+ </dependency>
<!-- ApacheDS (LDAP) -->
<dependency>
@@ -412,7 +437,7 @@
</dependency>
- <!-- Test -->
+ <!-- Test -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
Added: incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/exception/EmailException.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/exception/EmailException.java?rev=1292365&view=auto
==============================================================================
--- incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/exception/EmailException.java (added)
+++ incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/exception/EmailException.java Wed Feb 22 16:05:57 2012
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.rave.exception;
+
+/**
+ * @version "$Id$"
+ */
+public class EmailException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public EmailException() {
+ }
+
+ public EmailException(String message) {
+ super(message);
+ }
+
+ public EmailException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public EmailException(Throwable cause) {
+ super(cause);
+ }
+}
Modified: incubator/rave/trunk/rave-components/rave-core/pom.xml
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-core/pom.xml?rev=1292365&r1=1292364&r2=1292365&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-core/pom.xml (original)
+++ incubator/rave/trunk/rave-components/rave-core/pom.xml Wed Feb 22 16:05:57 2012
@@ -144,6 +144,20 @@
<artifactId>recaptcha4j</artifactId>
</dependency>
+ <!-- mail -->
+ <dependency>
+ <groupId>javax.mail</groupId>
+ <artifactId>mail</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context-support</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.freemarker</groupId>
+ <artifactId>freemarker</artifactId>
+ </dependency>
+
<!-- Test -->
<dependency>
Modified: incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/NewUser.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/NewUser.java?rev=1292365&r1=1292364&r2=1292365&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/NewUser.java (original)
+++ incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/NewUser.java Wed Feb 22 16:05:57 2012
@@ -38,6 +38,7 @@ public class NewUser {
private String displayName;
private String status;
private String aboutMe;
+ private String forgotPasswordHash;
public NewUser(){
}
@@ -181,5 +182,12 @@ public class NewUser {
public void setAboutMe(String aboutMe) {
this.aboutMe = aboutMe;
}
-
+
+ public String getForgotPasswordHash() {
+ return forgotPasswordHash;
+ }
+
+ public void setForgotPasswordHash(String forgotPasswordHash) {
+ this.forgotPasswordHash = forgotPasswordHash;
+ }
}
\ No newline at end of file
Modified: incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/User.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/User.java?rev=1292365&r1=1292364&r2=1292365&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/User.java (original)
+++ incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/model/User.java Wed Feb 22 16:05:57 2012
@@ -27,6 +27,7 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Date;
/**
* {@inheritDoc}
@@ -38,6 +39,7 @@ import java.util.Collections;
@NamedQuery(name = User.USER_GET_BY_USERNAME, query = "select u from User u where u.username = :"+User.PARAM_USERNAME),
@NamedQuery(name = User.USER_GET_BY_USER_EMAIL, query = "select u from User u where u.email = :"+User.PARAM_EMAIL),
@NamedQuery(name = User.USER_GET_ALL, query = "select u from User u order by u.username asc"),
+ @NamedQuery(name = User.USER_GET_BY_FORGOT_PASSWORD_HASH, query = "select u from User u where u.forgotPasswordHash = :" + User.PARAM_FORGOT_PASSWORD_HASH),
@NamedQuery(name = User.USER_COUNT_ALL, query = "select count(u) from User u"),
@NamedQuery(name = User.USER_FIND_BY_USERNAME_OR_EMAIL, query = "select u from User u " +
"where lower(u.username) like :"+User.PARAM_SEARCHTERM+" or lower(u.email) like :"+User.PARAM_SEARCHTERM+" order by u.username asc"),
@@ -56,8 +58,10 @@ public class User extends Person impleme
public static final String USER_COUNT_FIND_BY_USERNAME_OR_EMAIL = "User.countFindByUsernameOrEmail";
public static final String USER_GET_COMMENTERS = "User.getCommenters";
public static final String USER_GET_ALL_FOR_ADDED_WIDGET = "User.getAllForAddedWidget";
+ public static final String USER_GET_BY_FORGOT_PASSWORD_HASH = "User.getByForgotPasswordHash";
public static final String PARAM_USERNAME = "username";
+ public static final String PARAM_FORGOT_PASSWORD_HASH = "forgotPasswordHash";
public static final String PARAM_EMAIL = "email";
public static final String PARAM_SEARCHTERM = "searchTerm";
public static final String PARAM_WIDGET_ID = "widgetId";
@@ -81,8 +85,17 @@ public class User extends Person impleme
@Basic
@Column(name = "openid")
- private String openId;
-
+ private String openId;
+
+ @Basic
+ @Column(name = "forgotPasswordHash", unique = true)
+ private String forgotPasswordHash;
+
+ @Basic
+ @Column(name = "password_hash_time")
+ @Temporal(TemporalType.TIMESTAMP)
+ private Date forgotPasswordTime;
+
@ManyToOne
@JoinColumn(name="default_page_layout_id")
private PageLayout defaultPageLayout;
@@ -239,7 +252,23 @@ public class User extends Person impleme
public void setOpenId(String openId) {
this.openId = openId;
}
-
+
+ public String getForgotPasswordHash() {
+ return forgotPasswordHash;
+ }
+
+ public void setForgotPasswordHash(String forgotPasswordHash) {
+ this.forgotPasswordHash = forgotPasswordHash;
+ }
+
+ public Date getForgotPasswordTime() {
+ return forgotPasswordTime;
+ }
+
+ public void setForgotPasswordTime(Date forgotPasswordTime) {
+ this.forgotPasswordTime = forgotPasswordTime;
+ }
+
public PageLayout getDefaultPageLayout() {
return defaultPageLayout;
}
Modified: incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/repository/UserRepository.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/repository/UserRepository.java?rev=1292365&r1=1292364&r2=1292365&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/repository/UserRepository.java (original)
+++ incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/repository/UserRepository.java Wed Feb 22 16:05:57 2012
@@ -21,6 +21,7 @@ package org.apache.rave.portal.repositor
import org.apache.rave.persistence.Repository;
import org.apache.rave.portal.model.User;
+import java.util.Date;
import java.util.List;
public interface UserRepository extends Repository<User> {
@@ -80,4 +81,14 @@ public interface UserRepository extends
* @return List of User objects in alphabetical order sorted by familyname, givenname
*/
List<User> getAllByAddedWidget(long widgetId);
+
+ /**
+ * Gets a {@link User} by generated forgot email hash
+ *
+ * @param hash unique generated hash
+ * @return {@link User} if one exists for given hash, otherwise {@literal null}
+ */
+ User getByForgotPasswordHash(String hash);
+
}
+
Modified: incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/repository/impl/JpaUserRepository.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/repository/impl/JpaUserRepository.java?rev=1292365&r1=1292364&r2=1292365&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/repository/impl/JpaUserRepository.java (original)
+++ incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/repository/impl/JpaUserRepository.java Wed Feb 22 16:05:57 2012
@@ -29,6 +29,8 @@ import org.springframework.stereotype.Re
import javax.persistence.Query;
import javax.persistence.TypedQuery;
+
+import java.util.Date;
import java.util.List;
import static org.apache.rave.persistence.jpa.util.JpaUtil.getPagedResultList;
@@ -91,4 +93,12 @@ public class JpaUserRepository extends A
query.setParameter(User.PARAM_WIDGET_ID, widgetId);
return query.getResultList();
}
+
+ @Override
+ public User getByForgotPasswordHash(String hash) {
+ TypedQuery<User> query = manager.createNamedQuery(User.USER_GET_BY_FORGOT_PASSWORD_HASH, User.class);
+ query.setParameter(User.PARAM_FORGOT_PASSWORD_HASH, hash);
+ return getSingleResult(query.getResultList());
+ }
+
}
Added: incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/EmailService.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/EmailService.java?rev=1292365&view=auto
==============================================================================
--- incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/EmailService.java (added)
+++ incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/EmailService.java Wed Feb 22 16:05:57 2012
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.rave.portal.service;
+
+import java.util.Map;
+
+/**
+ * @version "$Id$"
+ */
+public interface EmailService {
+
+ /**
+ * Sends an email to specified recipient
+ *
+ * @param to recipient e.g. {@code John Doe<jo...@example.com>}
+ * @param subject email subject
+ * @param templateName name of the Freemarker template
+ * @param templateData data provided to Freemarker template
+ */
+ void sendEmail(String to, String subject, String templateName, Map<String, Object> templateData);
+}
Modified: incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/UserService.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/UserService.java?rev=1292365&r1=1292364&r2=1292365&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/UserService.java (original)
+++ incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/UserService.java Wed Feb 22 16:05:57 2012
@@ -19,13 +19,14 @@
package org.apache.rave.portal.service;
+import java.util.List;
+
+import org.apache.rave.portal.model.NewUser;
import org.apache.rave.portal.model.Person;
import org.apache.rave.portal.model.User;
import org.apache.rave.portal.model.util.SearchResult;
import org.springframework.security.core.userdetails.UserDetailsService;
-import java.util.List;
-
public interface UserService extends UserDetailsService {
/**
* Get the currently authenticated user.
@@ -97,8 +98,8 @@ public interface UserService extends Use
* Gets a {@link SearchResult} for {@link User}'s that match the search term
*
* @param searchTerm free text input to search on users
- * @param offset start point within the resultset (for paging)
- * @param pageSize maximum number of items to be returned (for paging)
+ * @param offset start point within the resultset (for paging)
+ * @param pageSize maximum number of items to be returned (for paging)
* @return SearchResult
*/
SearchResult<User> getUsersByFreeTextSearch(String searchTerm, int offset, int pageSize);
@@ -117,4 +118,38 @@ public interface UserService extends Use
* @return List of Person objects in alphabetical order sorted by familyname, givenname
*/
List<Person> getAllByAddedWidget(long widgetId);
+
+ /**
+ * Sends an email which contains link for changing user password
+ *
+ * @param user the {@link org.apache.rave.portal.model.NewUser} which requested password change
+ */
+ void sendPasswordReminder(NewUser user);
+
+ /**
+ * Sends an email which contains username
+ *
+ * @param user the {@link org.apache.rave.portal.model.NewUser} which requested username reminder
+ */
+ void sendUserNameReminder(NewUser user);
+
+
+ /**
+ * Changes password for given user
+ *
+ * @param user the {@link org.apache.rave.portal.model.NewUser} which requested password change
+ * @throws Exception in case something goes wrong
+ */
+ void updatePassword(NewUser user);
+
+ /**
+ * Check if username/email reminder request is still valid (not expired)
+ *
+ * @param forgotPasswordHash hash provided by user
+ * @return true if forgotPasswordHash is stil within given time range
+ * @throws Exception in case something goes wrong
+ */
+ boolean isValidReminderRequest(String forgotPasswordHash, int nrOfMinutesValid);
+
+
}
\ No newline at end of file
Added: incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultEmailService.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultEmailService.java?rev=1292365&view=auto
==============================================================================
--- incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultEmailService.java (added)
+++ incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultEmailService.java Wed Feb 22 16:05:57 2012
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.rave.portal.service.impl;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.rave.exception.EmailException;
+import org.apache.rave.portal.service.EmailService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.mail.MailMessage;
+import org.springframework.mail.SimpleMailMessage;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.stereotype.Service;
+import org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean;
+import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
+
+import freemarker.template.Configuration;
+import freemarker.template.TemplateException;
+
+/**
+ * Default implementation of EmailService
+ *
+ * @version "$Id$"
+ */
+@Service
+public class DefaultEmailService implements EmailService {
+
+ @Autowired
+ private SimpleMailMessage emailServiceMailMessage;
+
+ @Autowired
+ private JavaMailSender mailSender;
+
+ @Autowired
+ private FreeMarkerConfigurationFactoryBean freemarkerMailConfiguration;
+
+
+ private static Logger log = LoggerFactory.getLogger(DefaultEmailService.class);
+
+ @Override
+ public void sendEmail(String to, String subject, String templateName, Map<String, Object> templateData) {
+ // create a copy of mail message:
+ SimpleMailMessage message = new SimpleMailMessage(emailServiceMailMessage);
+ message.setSubject(subject);
+ message.setTo(to);
+ // set body text:
+ Configuration configuration = freemarkerMailConfiguration.getObject();
+ String text = parseTemplate(configuration, templateData, templateName);
+ message.setText(text);
+ // send email:
+ mailSender.send(message);
+ }
+
+ private String parseTemplate(Configuration configuration, Map<String, Object> templateData, String templateName) {
+
+ try {
+ return FreeMarkerTemplateUtils.processTemplateIntoString(configuration.getTemplate(templateName), templateData);
+ } catch (IOException e) {
+ if (log.isDebugEnabled()) {
+ log.error("Error parsing email template" + templateName, e);
+ }
+ throw new EmailException("Username reminder: error parsing email template" + templateName);
+ } catch (TemplateException e) {
+ if (log.isDebugEnabled()) {
+ log.error("failed to render email template " + templateName, e);
+ }
+ throw new EmailException("Username reminder: error rendering email template: " + templateName);
+ }
+ }
+
+
+
+}
Modified: incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultUserService.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultUserService.java?rev=1292365&r1=1292364&r2=1292365&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultUserService.java (original)
+++ incubator/rave/trunk/rave-components/rave-core/src/main/java/org/apache/rave/portal/service/impl/DefaultUserService.java Wed Feb 22 16:05:57 2012
@@ -19,15 +19,22 @@
package org.apache.rave.portal.service.impl;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.rave.portal.model.NewUser;
+
import org.apache.rave.portal.model.PageType;
+
import org.apache.rave.portal.model.Person;
import org.apache.rave.portal.model.User;
import org.apache.rave.portal.model.util.SearchResult;
import org.apache.rave.portal.repository.*;
+import org.apache.rave.portal.service.EmailService;
import org.apache.rave.portal.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.Authentication;
@@ -36,11 +43,14 @@ import org.springframework.security.core
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.crypto.codec.Base64;
+import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import java.util.ArrayList;
-import java.util.List;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.*;
/**
*
@@ -56,6 +66,27 @@ public class DefaultUserService implemen
private final WidgetRepository widgetRepository;
@Autowired
+ private PasswordEncoder passwordEncoder;
+
+ @Autowired
+ private EmailService emailService;
+
+ @Value("${portal.mail.passwordservice.subject}")
+ private String passwordReminderSubject;
+
+ @Value("${portal.mail.passwordservice.template}")
+ private String passwordReminderTemplate;
+
+ @Value("${portal.mail.username.subject}")
+ private String userNameReminderSubject;
+
+ @Value("${portal.mail.username.template}")
+ private String userNameReminderTemplate;
+
+ @Value("${portal.mail.service.baseurl}")
+ private String baseUrl;
+
+ @Autowired
public DefaultUserService(UserRepository userRepository,
PageRepository pageRepository,
WidgetRatingRepository widgetRatingRepository,
@@ -107,6 +138,8 @@ public class DefaultUserService implemen
private SecurityContext createContext(final User user) {
SecurityContext securityContext = new SecurityContextImpl();
securityContext.setAuthentication(new AbstractAuthenticationToken(user.getAuthorities()) {
+ private static final long serialVersionUID = 1L;
+
@Override
public Object getCredentials() {
return "N/A";
@@ -181,7 +214,7 @@ public class DefaultUserService implemen
log.warn("unable to find userId " + userId + " to delete");
return;
}
-
+
final String username = user.getUsername();
// delete all User type pages
@@ -194,7 +227,7 @@ public class DefaultUserService implemen
int numWidgetsOwned = widgetRepository.unassignWidgetOwner(userId);
// finally delete the user
userRepository.delete(user);
- log.info("Deleted user [" + userId + "," + username + "] - numPages: " + numDeletedPages +
+ log.info("Deleted user [" + userId + ',' + username + "] - numPages: " + numDeletedPages +
", numWidgetComments: " + numWidgetComments + ", numWidgetRatings: " + numWidgetRatings +
", numWidgetsOwned: " + numWidgetsOwned);
}
@@ -208,4 +241,84 @@ public class DefaultUserService implemen
}
return persons;
}
+
+ @Override
+ public void updatePassword(NewUser newUser) {
+ log.debug("Changing password for user {}", newUser);
+ User user = userRepository.getByForgotPasswordHash(newUser.getForgotPasswordHash());
+ if (user == null) {
+ throw new IllegalArgumentException("Could not find user for forgotPasswordHash " + newUser.getForgotPasswordHash());
+ }
+ String saltedHashedPassword = passwordEncoder.encode(newUser.getPassword());
+ user.setPassword(saltedHashedPassword);
+ // reset password hash and time
+ user.setForgotPasswordHash(null);
+ user.setForgotPasswordTime(null);
+ userRepository.save(user);
+
+ }
+
+
+ @Override
+ public void sendUserNameReminder(NewUser newUser) {
+ log.debug("Calling send username {}", newUser);
+ User user = userRepository.getByUserEmail(newUser.getEmail());
+ if (user == null) {
+ throw new IllegalArgumentException("Could not find user for email " + newUser.getEmail());
+ }
+ String to = user.getUsername() + " <" + user.getEmail() + '>';
+ Map<String, Object> templateData = new HashMap<String, Object>();
+ templateData.put("user", user);
+ emailService.sendEmail(to, userNameReminderSubject, userNameReminderTemplate, templateData);
+
+ }
+
+
+ @Override
+ public void sendPasswordReminder(NewUser newUser) {
+ log.debug("Calling send password change link for user {}", newUser);
+ User user = userRepository.getByUserEmail(newUser.getEmail());
+ if (user == null) {
+ throw new IllegalArgumentException("Could not find user for email " + newUser.getEmail());
+ }
+ // create user hash:
+ String input = user.getEmail() + user.getUsername() + String.valueOf(user.getEntityId()) + System.nanoTime();
+ // hash needs to be URL friendly:
+ String safeString = new String(Base64.encode(passwordEncoder.encode(input).getBytes()));
+ String hashedInput = safeString.replaceAll("[/=]", "A");
+ user.setForgotPasswordHash(hashedInput);
+ user.setForgotPasswordTime(Calendar.getInstance().getTime());
+ userRepository.save(user);
+ String to = user.getUsername() + " <" + user.getEmail() + '>';
+ Map<String, Object> templateData = new HashMap<String, Object>();
+ templateData.put("user", user);
+ templateData.put("reminderUrl", baseUrl + hashedInput);
+ emailService.sendEmail(to, passwordReminderSubject, passwordReminderTemplate, templateData);
+ }
+
+
+ @Override
+ public boolean isValidReminderRequest(String forgotPasswordHash, int nrOfMinutesValid) {
+ if (StringUtils.isBlank(forgotPasswordHash)) {
+ return false;
+ }
+
+ User userForHash = userRepository.getByForgotPasswordHash(forgotPasswordHash);
+ if (userForHash == null) {
+ return false;
+ }
+ Date requestTime = userForHash.getForgotPasswordTime();
+ Calendar expiredDate = Calendar.getInstance();
+ expiredDate.add(Calendar.MINUTE, nrOfMinutesValid);
+
+ if (requestTime == null || requestTime.after(expiredDate.getTime())) {
+ // reset, this is invalid state
+ userForHash.setForgotPasswordHash(null);
+ userForHash.setForgotPasswordTime(null);
+ userRepository.save(userForHash);
+ return false;
+ }
+ return true;
+ }
+
}
\ No newline at end of file
Modified: incubator/rave/trunk/rave-components/rave-core/src/main/resources/org/apache/rave/core-applicationContext.xml
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-core/src/main/resources/org/apache/rave/core-applicationContext.xml?rev=1292365&r1=1292364&r2=1292365&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-core/src/main/resources/org/apache/rave/core-applicationContext.xml (original)
+++ incubator/rave/trunk/rave-components/rave-core/src/main/resources/org/apache/rave/core-applicationContext.xml Wed Feb 22 16:05:57 2012
@@ -24,12 +24,11 @@
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
- xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
- http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
+
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- make the the portal.properties props available to autowire injectors, location of the properties can
@@ -44,7 +43,7 @@
<context:annotation-config/>
<!-- enable the use of the @AspectJ style of Spring AOP -->
- <aop:aspectj-autoproxy />
+ <aop:aspectj-autoproxy/>
<!-- rave-common component base-package scan (maybe move to a separate common-applicationContext.xml?) -->
<context:component-scan base-package="org.apache.rave.service"/>
@@ -55,18 +54,18 @@
<context:component-scan base-package="org.apache.rave.portal.repository"/>
<context:component-scan base-package="org.apache.rave.portal.service"/>
<context:component-scan base-package="org.apache.rave.portal.security"/>
-
+
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
- <tx:annotation-driven transaction-manager="transactionManager" />
+ <tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="entityManagerFactory"
class="org.apache.rave.persistence.jpa.PopulatedLocalContainerEntityManagerFactory">
- <property name="populator" ref="dataSourcePopulator" />
+ <property name="populator" ref="dataSourcePopulator"/>
<property name="loadTimeWeaver">
- <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
+ <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
</property>
<property name="persistenceUnitName" value="ravePersistenceUnit"/>
<property name="dataSource" ref="dataSource"/>
@@ -94,10 +93,49 @@
<property name="username" value="${portal.dataSource.username}"/>
<property name="password" value="${portal.dataSource.password}"/>
</bean>
-
+
<!-- Password encoding -->
<bean class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" id="passwordEncoder">
<!--<constructor-arg index="0" value="10"/>-->
</bean>
+ <!-- email settings -->
+ <bean id="emailServiceMailMessage" class="org.springframework.mail.SimpleMailMessage">
+ <property name="from" value="${portal.mail.sender}"/>
+ <property name="replyTo" value="${portal.mail.replyto}"/>
+ </bean>
+
+ <bean id="freemarkerMailConfiguration" class="org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean">
+ <property name="templateLoaderPath" value="/WEB-INF/mailtemplates"/>
+ </bean>
+ <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
+ <property name="host" value="${portal.mail.host}"/>
+ <property name="password" value="${portal.mail.password}"/>
+ <property name="username" value="${portal.mail.username}"/>
+ <property name="port" value="${portal.mail.port}"/>
+ <property name="protocol" value="${portal.mail.protocol}"/>
+ <!-- NOTE: if using Gmail, you'll need following properties-->
+ <!--<property name="javaMailProperties">
+ <props>
+ <prop key="mail.smtp.auth">true</prop>
+ <prop key="mail.smtp.starttls.enable">true</prop>
+ <prop key="mail.smtp.timeout">8500</prop>
+ </props>
+ </property>-->
+ </bean>
+ <!--
+ NOTE: to use mail session you'll need to configure following within catalina_home/conf/context.xml
+ <Resource name="mail/Session" auth="Container" type="javax.mail.Session" mail.smtp.host="my.mail.host"/>
+
+ Further, activation & mail jars needs to be placed within catalina_home/lib folder
+ -->
+ <!--
+ <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
+ <property name="session" ref="mailSession"/>
+ </bean>
+ <bean id="mailSession" class="org.springframework.jndi.JndiObjectFactoryBean">
+ <property name="jndiName" value="java:comp/env/mail/Session"/>
+ </bean>
+ -->
+
</beans>
\ No newline at end of file
Modified: incubator/rave/trunk/rave-components/rave-core/src/test/resources/portal.properties
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-core/src/test/resources/portal.properties?rev=1292365&r1=1292364&r2=1292365&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-core/src/test/resources/portal.properties (original)
+++ incubator/rave/trunk/rave-components/rave-core/src/test/resources/portal.properties Wed Feb 22 16:05:57 2012
@@ -43,4 +43,19 @@ portal.captcha.enabled=false
portal.captcha.key.private=
portal.captcha.key.public=
portal.captcha.usenoscript=false
-portal.captcha.invalid.configuration=<label class="error">ReCaptcha service is not properly configured.</label>
\ No newline at end of file
+portal.captcha.invalid.configuration=<label class="error">ReCaptcha service is not properly configured.</label>
+
+#mail settings
+portal.mail.sender=
+portal.mail.replyto=
+portal.mail.host=
+portal.mail.password=
+portal.mail.username=
+portal.mail.protocol=smtp
+portal.mail.port=25
+portal.mail.username.subject=Rave username reminder service
+portal.mail.username.template=username_reminder.ftl
+portal.mail.passwordservice.subject=Rave password reminder service
+portal.mail.passwordservice.template=password_reminder.ftl
+portal.mail.passwordservice.valid.minutes=30
+portal.mail.service.baseurl=http://localhost:8080/portal/app/changepassword/
Added: incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/ChangePasswordController.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/ChangePasswordController.java?rev=1292365&view=auto
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/ChangePasswordController.java (added)
+++ incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/ChangePasswordController.java Wed Feb 22 16:05:57 2012
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.rave.portal.web.controller;
+
+import org.apache.rave.portal.model.NewUser;
+import org.apache.rave.portal.service.UserService;
+import org.apache.rave.portal.web.util.ModelKeys;
+import org.apache.rave.portal.web.util.ViewNames;
+import org.apache.rave.portal.web.validator.ChangePasswordValidator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+/**
+ * Controller which processes change password requests.
+ * Only requests that have a valid (matched) forgotPasswordHash will be honored
+ *
+ * @version "$Id$"
+ * @see org.apache.rave.portal.model.User#forgotPasswordHash
+ */
+@Controller
+public class ChangePasswordController {
+
+
+ private static Logger log = LoggerFactory.getLogger(ChangePasswordController.class);
+ private final UserService userService;
+ private final ChangePasswordValidator passwordValidator;
+
+
+ @Autowired
+ public ChangePasswordController(UserService userService, ChangePasswordValidator passwordValidator) {
+ this.userService = userService;
+ this.passwordValidator = passwordValidator;
+ }
+
+
+ @RequestMapping(value = {"/changepassword/{passwordHash:.*}"}, method = RequestMethod.GET)
+ public String initialize(Model model, @PathVariable("passwordHash") String passwordHash, RedirectAttributes redirectAttributes) {
+ log.debug("Requesting user for hash: {}", passwordHash);
+ NewUser user = new NewUser();
+ model.addAttribute(ModelKeys.NEW_USER, user);
+ user.setForgotPasswordHash(passwordHash);
+ return ViewNames.PASSWORD_CHANGE;
+ }
+
+
+ @RequestMapping(value = {"/changepassword", "/changepassword/**"}, method = RequestMethod.POST)
+ public String update(@ModelAttribute NewUser newUser, BindingResult results, Model model) {
+ log.debug("updating user password for hash {}", newUser.getForgotPasswordHash());
+ model.addAttribute(ModelKeys.NEW_USER, newUser);
+ passwordValidator.validate(newUser, results);
+
+ if (results.hasErrors()) {
+ log.info("changepassword, request contains validation errors");
+ return ViewNames.PASSWORD_CHANGE;
+ }
+ try {
+ log.debug("Submitted passwords were valid");
+ userService.updatePassword(newUser);
+ return ViewNames.REDIRECT;
+ } catch (Exception ex) {
+ results.reject("Unable to change password:" + ex.getMessage(), "Unable to change password.");
+ return ViewNames.PASSWORD_CHANGE;
+ }
+
+ }
+}
Modified: incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/NewAccountController.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/NewAccountController.java?rev=1292365&r1=1292364&r2=1292365&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/NewAccountController.java (original)
+++ incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/NewAccountController.java Wed Feb 22 16:05:57 2012
@@ -57,7 +57,7 @@ public class NewAccountController {
@RequestMapping(value = "/newaccount.jsp")
public void setUpForm(ModelMap model, HttpServletRequest request) {
logger.debug("Initializing form");
- model.addAttribute("captchaHtml", captchaService.createHtml(request));
+ model.addAttribute(ModelKeys.CAPTCHA_HTML, captchaService.createHtml(request));
model.addAttribute(ModelKeys.NEW_USER, new NewUser());
}
@@ -108,6 +108,6 @@ public class NewAccountController {
}
private void initializeCaptcha(Model model, HttpServletRequest request) {
- model.addAttribute("captchaHtml", captchaService.createHtml(request));
+ model.addAttribute(ModelKeys.CAPTCHA_HTML, captchaService.createHtml(request));
}
}
\ No newline at end of file
Added: incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/ReminderController.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/ReminderController.java?rev=1292365&view=auto
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/ReminderController.java (added)
+++ incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/controller/ReminderController.java Wed Feb 22 16:05:57 2012
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.rave.portal.web.controller;
+
+import org.apache.rave.portal.model.NewUser;
+import org.apache.rave.portal.service.CaptchaService;
+import org.apache.rave.portal.service.UserService;
+import org.apache.rave.portal.web.util.ModelKeys;
+import org.apache.rave.portal.web.util.ViewNames;
+import org.apache.rave.portal.web.validator.NewPasswordValidator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
+import org.springframework.ui.ModelMap;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Handles password ans username reminder requests
+ *
+ * @version "$Id$"
+ */
+@Controller
+public class ReminderController {
+
+ private static Logger log = LoggerFactory.getLogger(ReminderController.class);
+
+ private final UserService userService;
+ private final NewPasswordValidator passwordValidator;
+ private final CaptchaService captchaService;
+
+
+ @Autowired
+ protected ReminderController(UserService userService, NewPasswordValidator passwordValidator, CaptchaService captchaService) {
+ this.userService = userService;
+ this.passwordValidator = passwordValidator;
+ this.captchaService = captchaService;
+ }
+
+
+ @RequestMapping(value = {"/retrieveusername", "/newpassword"})
+ public void initialize(ModelMap model, HttpServletRequest request) {
+ model.addAttribute(ModelKeys.CAPTCHA_HTML, captchaService.createHtml(request));
+ model.addAttribute(ModelKeys.NEW_USER, new NewUser());
+ }
+
+
+ /**
+ * Processes username requests
+ */
+ @RequestMapping(value = {"/retrieveusername"}, method = RequestMethod.POST)
+ public String requestUsername(@ModelAttribute NewUser newUser, BindingResult results, Model model,
+ HttpServletRequest request, RedirectAttributes redirectAttributes) {
+ log.debug("Requesting username reminder");
+ if (!validateEmail(newUser, results, model, request)) {
+ return captchaRequest(model, request, ViewNames.USERNAME_REQUEST);
+ }
+ try {
+ userService.sendUserNameReminder(newUser);
+ populateRedirect(newUser, redirectAttributes);
+ return ViewNames.REDIRECT_RETRIEVE_USERNAME;
+ } catch (Exception e) {
+ if (log.isDebugEnabled()) {
+ log.error("Exception while sending username reminder {}", e);
+ }
+ results.reject("Unable to send username reminder :" + e.getMessage(), "Unable to send username reminder.");
+ return captchaRequest(model, request, ViewNames.USERNAME_REQUEST);
+ }
+ }
+
+
+ /**
+ * Processes new password requests
+ */
+ @RequestMapping(value = {"/newpassword"}, method = RequestMethod.POST)
+ public String requestPassword(@ModelAttribute NewUser newUser, BindingResult results, Model model,
+ HttpServletRequest request, RedirectAttributes redirectAttributes) {
+ log.debug("Requesting password reminder");
+ if (!validateEmail(newUser, results, model, request)) {
+ return captchaRequest(model, request, ViewNames.NEW_PASSWORD_REQUEST);
+ }
+ try {
+ userService.sendPasswordReminder(newUser);
+ populateRedirect(newUser, redirectAttributes);
+ return ViewNames.REDIRECT_NEW_PASSWORD;
+ } catch (Exception e) {
+ if (log.isDebugEnabled()) {
+ log.error("Exception while sending password reminder {}", e);
+ }
+ results.reject("Unable to send password reminder :" + e.getMessage(), "Unable to send password reminder.");
+ return captchaRequest(model, request, ViewNames.NEW_PASSWORD_REQUEST);
+ }
+
+
+ }
+
+
+ private boolean validateEmail(NewUser newUser, BindingResult results, Model model, HttpServletRequest request) {
+ model.addAttribute(ModelKeys.NEW_USER, newUser);
+ passwordValidator.validate(newUser, results);
+ if (results.hasErrors()) {
+ log.info("newpassword request contains validation errors");
+ return false;
+ }
+
+ String email = newUser.getEmail();
+ log.debug("Submitted email {} is valid", email);
+ if (!captchaService.isValid(request)) {
+ log.debug("Captcha was invalid for user with email {}", email);
+ return false;
+ }
+ return true;
+ }
+
+
+ private void populateRedirect(NewUser newUser, RedirectAttributes redirectAttributes) {
+ redirectAttributes.addFlashAttribute("success", true);
+ redirectAttributes.addFlashAttribute("email", newUser.getEmail());
+ }
+
+ private String captchaRequest(Model model, HttpServletRequest request, String view) {
+ model.addAttribute(ModelKeys.CAPTCHA_HTML, captchaService.createHtml(request));
+ return view;
+ }
+
+}
Modified: incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/util/ModelKeys.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/util/ModelKeys.java?rev=1292365&r1=1292364&r2=1292365&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/util/ModelKeys.java (original)
+++ incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/util/ModelKeys.java Wed Feb 22 16:05:57 2012
@@ -49,4 +49,5 @@ public class ModelKeys {
public static final String CATEGORIES = "categories";
public static final String SELECTED_CATEGORY = "selectedCategory";
public static final String DEFAULT_TAG_PAGE = "defaultTagPage";
+ public static final String CAPTCHA_HTML = "captchaHtml";
}
\ No newline at end of file
Modified: incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/util/ViewNames.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/util/ViewNames.java?rev=1292365&r1=1292364&r2=1292365&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/util/ViewNames.java (original)
+++ incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/util/ViewNames.java Wed Feb 22 16:05:57 2012
@@ -22,7 +22,7 @@ package org.apache.rave.portal.web.util;
/**
* Defines constants representing the available view names in the system
*/
-public class ViewNames {
+public final class ViewNames {
private ViewNames() {}
private static final String USER_PREFIX = "templates.user.";
private static final String ADMIN_PREFIX = "templates.admin.";
@@ -47,16 +47,27 @@ public class ViewNames {
public static final String ADMIN_CATEGORIES = ADMIN_PREFIX + "categories";
public static final String ADMIN_CATEGORY_DETAIL = ADMIN_PREFIX + "categoryDetail";
+ // password reminder / changing
+ public static final String NEW_PASSWORD_REQUEST = USER_PREFIX + "newpassword";
+ public static final String USERNAME_REQUEST = USER_PREFIX + "retrieveusername";
+ public static final String PASSWORD_CHANGE = USER_PREFIX + "changepassword";
+
+
+
public static final String REDIRECT = "redirect:/";
public static final String POSTS_TAG_PAGE = "postsTagPage";
public static final String ABOUT_TAG_PAGE = "aboutTagPage";
+ public static final String REDIRECT_NEW_PASSWORD = REDIRECT + "app/newpassword";
+ public static final String REDIRECT_RETRIEVE_USERNAME = REDIRECT + "app/retrieveusername";
+
+
public static String getPageView(String layoutName) {
- return new StringBuilder(PAGE).append(".").append(layoutName).toString();
+ return new StringBuilder(PAGE).append('.').append(layoutName).toString();
}
public static String getPersonPageView(String layoutName) {
return new StringBuilder(PERSON_PROFILE).append(".").append(layoutName).toString();
}
-}
+}
\ No newline at end of file
Added: incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/ChangePasswordValidator.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/ChangePasswordValidator.java?rev=1292365&view=auto
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/ChangePasswordValidator.java (added)
+++ incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/ChangePasswordValidator.java Wed Feb 22 16:05:57 2012
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.rave.portal.web.validator;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.rave.portal.model.NewUser;
+import org.apache.rave.portal.service.UserService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.validation.Errors;
+
+/**
+ * Validates new password changes
+ * @version "$Id$"
+ */
+@Component
+public class ChangePasswordValidator extends NewAccountValidator {
+
+
+ private static Logger log = LoggerFactory.getLogger(ChangePasswordValidator.class);
+
+ @Value("${portal.mail.passwordservice.valid.minutes}")
+ private int minutesValid;
+
+ @Autowired
+ public ChangePasswordValidator(UserService userService) {
+ super(userService);
+ }
+
+ @Override
+ public void validate(Object target, Errors errors) {
+ log.debug("ChangePasswordValidator validator called");
+ NewUser newUser = (NewUser) target;
+ boolean validHash = getUserService().isValidReminderRequest(newUser.getForgotPasswordHash(), minutesValid);
+ if (!validHash) {
+ errors.rejectValue(FIELD_PASSWORD, "page.changepassword.expired");
+ // skip further validating anything else, does not make sense
+ return;
+ }
+
+ // we only check for password:
+ validatePassword(errors, newUser);
+ validateConfirmPassword(errors, newUser);
+
+ }
+
+}
Modified: incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/NewAccountValidator.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/NewAccountValidator.java?rev=1292365&r1=1292364&r2=1292365&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/NewAccountValidator.java (original)
+++ incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/NewAccountValidator.java Wed Feb 22 16:05:57 2012
@@ -30,17 +30,20 @@ import org.springframework.validation.Er
import org.springframework.validation.ObjectError;
import org.springframework.validation.Validator;
+import java.util.regex.Pattern;
+
@Component
public class NewAccountValidator implements Validator {
private static final int MINIMUM_PASSWORD_LENGTH = 4;
private static final String FIELD_USERNAME = "username";
- private static final String FIELD_PASSWORD = "password";
+ protected static final String FIELD_PASSWORD = "password";
private static final String FIELD_CONFIRM_PASSWORD = "confirmPassword";
- private static final String FIELD_EMAIL = "email";
+ protected static final String FIELD_EMAIL = "email";
private final Logger logger = LoggerFactory.getLogger(getClass());
+ private static final String USERNAME_MASK = "[\\w\\+\\-\\.@]{2,}";
- private static final String USERNAME_PATTERN = "[\\w\\+\\-\\.@]{2,}";
+ private static final Pattern USERNAME_PATTERN = Pattern.compile(USERNAME_MASK);
private UserService userService;
@Autowired
@@ -48,7 +51,7 @@ public class NewAccountValidator impleme
this.userService = userService;
}
- public boolean supports(Class aClass) {
+ public boolean supports(Class<?> aClass) {
return NewUser.class.isAssignableFrom(aClass);
}
@@ -69,7 +72,7 @@ public class NewAccountValidator impleme
if (StringUtils.isBlank(username)) {
errors.rejectValue(FIELD_USERNAME, "username.required");
logger.info("Username required");
- } else if (!username.matches(USERNAME_PATTERN)) {
+ } else if (!USERNAME_PATTERN.matcher(username).matches()) {
errors.rejectValue(FIELD_USERNAME, "username.invalid.pattern");
logger.info("Username has invalid pattern");
} else if (isExistingUsername(username)) {
@@ -82,7 +85,7 @@ public class NewAccountValidator impleme
return userService.getUserByUsername(username) != null;
}
- private void validatePassword(Errors errors, NewUser newUser) {
+ protected void validatePassword(Errors errors, NewUser newUser) {
if (StringUtils.isBlank(newUser.getPassword())) {
errors.rejectValue(FIELD_PASSWORD, "password.required");
logger.info("Password required");
@@ -92,7 +95,7 @@ public class NewAccountValidator impleme
}
}
- private void validateConfirmPassword(Errors errors, NewUser newUser) {
+ protected void validateConfirmPassword(Errors errors, NewUser newUser) {
if (StringUtils.isBlank(newUser.getConfirmPassword())) {
errors.rejectValue(FIELD_CONFIRM_PASSWORD, "confirmPassword.required");
logger.info("Confirm Password required");
@@ -116,15 +119,15 @@ public class NewAccountValidator impleme
}
}
- private boolean isInvalidEmailAddress(String emailAddress) {
+ protected boolean isInvalidEmailAddress(String emailAddress) {
return !EmailValidator.getInstance().isValid(emailAddress);
}
- private boolean isExistingEmailAddress(String email) {
+ protected boolean isExistingEmailAddress(String email) {
return userService.getUserByEmail(email) != null;
}
- private void writeResultToLog(Errors errors) {
+ protected void writeResultToLog(Errors errors) {
if (errors.hasErrors()) {
if (logger.isInfoEnabled()) {
for (ObjectError error : errors.getAllErrors()) {
@@ -135,4 +138,8 @@ public class NewAccountValidator impleme
logger.debug("Validation successful");
}
}
+
+ public UserService getUserService() {
+ return userService;
+ }
}
\ No newline at end of file
Added: incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/NewPasswordValidator.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/NewPasswordValidator.java?rev=1292365&view=auto
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/NewPasswordValidator.java (added)
+++ incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/validator/NewPasswordValidator.java Wed Feb 22 16:05:57 2012
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.rave.portal.web.validator;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.rave.portal.model.NewUser;
+import org.apache.rave.portal.model.User;
+import org.apache.rave.portal.service.UserService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.validation.Errors;
+
+/**
+ * Validates password change requests. Valid requests are only ones for users we can find by email address
+ *
+ * @version "$Id$"
+ */
+@Component
+public class NewPasswordValidator extends NewAccountValidator {
+
+ private static Logger log = LoggerFactory.getLogger(NewPasswordValidator.class);
+
+
+ @Autowired
+ public NewPasswordValidator(UserService userService) {
+ super(userService);
+ }
+
+ @Override
+ public void validate(Object target, Errors errors) {
+ log.debug("Password validator called");
+ NewUser newUser = (NewUser) target;
+ // we only check for existing (and valid) email
+ String email = newUser.getEmail();
+ validateEmail(errors, email);
+ if (errors.hasErrors()) {
+ return;
+ }
+ // check if account exists and if it is locked or expired:
+ User user = getUserService().getUserByEmail(email);
+ if (user == null) {
+ errors.rejectValue(FIELD_EMAIL, "account.invalid");
+ log.info("Couldn't find user for email {}", email);
+ return;
+ }
+ if (user.isLocked() || user.isExpired() || !user.isEnabled()) {
+ errors.rejectValue(FIELD_EMAIL, "account.invalid");
+ }
+
+ }
+
+ private void validateEmail(Errors errors, String email) {
+ if (StringUtils.isBlank(email)) {
+ errors.rejectValue(FIELD_EMAIL, "email.required");
+ } else if (isInvalidEmailAddress(email)) {
+ errors.rejectValue(FIELD_EMAIL, "email.invalid");
+ } else if (!isExistingEmailAddress(email)) {
+ errors.rejectValue(FIELD_EMAIL, "email.doesnot.exist");
+ }
+ }
+
+
+}
Added: incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/ChangePasswordControllerTest.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/ChangePasswordControllerTest.java?rev=1292365&view=auto
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/ChangePasswordControllerTest.java (added)
+++ incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/ChangePasswordControllerTest.java Wed Feb 22 16:05:57 2012
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.rave.portal.web.controller;
+
+import org.apache.rave.portal.model.NewUser;
+import org.apache.rave.portal.service.UserService;
+import org.apache.rave.portal.web.util.ModelKeys;
+import org.apache.rave.portal.web.util.ViewNames;
+import org.apache.rave.portal.web.validator.ChangePasswordValidator;
+import org.hamcrest.CoreMatchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.ui.Model;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.DirectFieldBindingResult;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @version "$Id$"
+ */
+public class ChangePasswordControllerTest {
+
+ private ChangePasswordValidator passwordValidator;
+ private ChangePasswordController controller;
+
+ @Before
+ public void setUp() throws Exception {
+ UserService userService = createNiceMock(UserService.class);
+
+ expect(userService.isValidReminderRequest(null, 0)).andReturn(true).anyTimes();
+ replay(userService);
+ passwordValidator = new ChangePasswordValidator(userService);
+ controller = new ChangePasswordController(userService, passwordValidator);
+ }
+
+
+ @Test
+ public void testInitialize() throws Exception {
+ final Model model = createNiceMock(Model.class);
+ RedirectAttributes redirectAttributes = createNiceMock(RedirectAttributes.class);
+ replay(redirectAttributes);
+ replay(model);
+ String viewName = controller.initialize(model, null, redirectAttributes);
+ assertThat(viewName, CoreMatchers.equalTo(ViewNames.PASSWORD_CHANGE));
+ }
+
+ @Test
+ public void testUpdate() throws Exception {
+ final Model model = createNiceMock(Model.class);
+ RedirectAttributes redirectAttributes = createNiceMock(RedirectAttributes.class);
+ NewUser newUser = new NewUser();
+ replay(redirectAttributes);
+ replay(model);
+ BindingResult results = new DirectFieldBindingResult(newUser, ModelKeys.NEW_USER);
+ String viewName = controller.update(newUser, results, redirectAttributes);
+ assertThat(viewName, CoreMatchers.equalTo(ViewNames.PASSWORD_CHANGE));
+ assertThat(results.hasErrors(), CoreMatchers.equalTo(true));
+ assertThat(results.getErrorCount(), CoreMatchers.equalTo(2));
+ // invalid password, to short:
+ newUser.setPassword("123");
+ results = new DirectFieldBindingResult(newUser, ModelKeys.NEW_USER);
+ controller.update(newUser, results, redirectAttributes);
+ assertEquals("Expected password errors", 2, results.getErrorCount());
+ assertEquals("Expected password errors", "password.invalid.length", results.getFieldError().getCode());
+ // missing password confirm:
+ newUser.setPassword("1234");
+ results = new DirectFieldBindingResult(newUser, ModelKeys.NEW_USER);
+ controller.update(newUser, results, redirectAttributes);
+ assertEquals("Expected password errors", 1, results.getErrorCount());
+ assertEquals("Expected password errors", "confirmPassword.required", results.getFieldError().getCode());
+ // password confirm not equal:
+ newUser.setPassword("1234");
+ newUser.setConfirmPassword("12345");
+ results = new DirectFieldBindingResult(newUser, ModelKeys.NEW_USER);
+ controller.update(newUser, results, redirectAttributes);
+ assertEquals("Expected password errors", 1, results.getErrorCount());
+ assertEquals("Expected password errors", "confirmPassword.mismatch", results.getFieldError().getCode());
+
+ // ok request
+ newUser.setPassword("1234");
+ newUser.setConfirmPassword("1234");
+ results = new DirectFieldBindingResult(newUser, ModelKeys.NEW_USER);
+ controller.update(newUser, results, redirectAttributes);
+ assertEquals("Expected password errors", 0, results.getErrorCount());
+
+
+ }
+}
Added: incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/ReminderControllerTest.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/ReminderControllerTest.java?rev=1292365&view=auto
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/ReminderControllerTest.java (added)
+++ incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/controller/ReminderControllerTest.java Wed Feb 22 16:05:57 2012
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.rave.portal.web.controller;
+
+import org.apache.rave.portal.model.NewUser;
+import org.apache.rave.portal.service.CaptchaService;
+import org.apache.rave.portal.service.UserService;
+import org.apache.rave.portal.service.impl.ReCaptchaService;
+import org.apache.rave.portal.web.util.ModelKeys;
+import org.apache.rave.portal.web.util.ViewNames;
+import org.apache.rave.portal.web.validator.NewPasswordValidator;
+import org.hamcrest.CoreMatchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.ui.Model;
+import org.springframework.ui.ModelMap;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.DirectFieldBindingResult;
+import org.springframework.web.servlet.mvc.support.RedirectAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.replay;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ * @version "$Id$"
+ */
+public class ReminderControllerTest {
+
+ private CaptchaService captchaService;
+ private HttpServletRequest request;
+ private ReminderController controller;
+
+ @Before
+ public void setUp() throws Exception {
+
+ UserService userService = createNiceMock(UserService.class);
+ replay(userService);
+ request = new MockHttpServletRequest();
+ NewPasswordValidator passwordValidator = new NewPasswordValidator(userService);
+ captchaService = new ReCaptchaService(false, null, null, false, "error message");
+ controller = new ReminderController(userService, passwordValidator, captchaService);
+ }
+
+
+ @Test
+ public void testInitialize() throws Exception {
+ // test is model is added:
+ final ModelMap model = new ModelMap();
+ controller.initialize(model, request);
+ assertThat(model, CoreMatchers.notNullValue());
+ // captcha & user mode should be
+ assertEquals("Expected Captcha and NewUser model", 2, model.size());
+ assertThat(model.containsAttribute(ModelKeys.NEW_USER), CoreMatchers.equalTo(true));
+ assertThat(model.containsAttribute(ModelKeys.CAPTCHA_HTML), CoreMatchers.equalTo(true));
+ assertThat(model.get(ModelKeys.NEW_USER), CoreMatchers.notNullValue());
+ assertThat(model.get(ModelKeys.CAPTCHA_HTML), CoreMatchers.notNullValue());
+ assertThat(captchaService.isValid(request), CoreMatchers.equalTo(true));
+
+ }
+
+ @Test
+ public void testCreate() throws Exception {
+ Model model = createNiceMock(Model.class);
+ NewUser newUser = new NewUser();
+ BindingResult results = new DirectFieldBindingResult(newUser, ModelKeys.NEW_USER);
+ RedirectAttributes redirectAttributes = createNiceMock(RedirectAttributes.class);
+ replay(redirectAttributes);
+ replay(model);
+
+ // user part
+ // required email
+ controller.requestUsername(newUser, results, model, request, redirectAttributes);
+ assertThat(captchaService.isValid(request), CoreMatchers.equalTo(true));
+ assertEquals("Expected email errors", 1, results.getErrorCount());
+ assertEquals("Expected email errors", "email.required", results.getFieldError().getCode());
+ // invalid email
+ results = new DirectFieldBindingResult(newUser, ModelKeys.NEW_USER);
+ newUser.setEmail("test_email");
+ controller.requestUsername(newUser, results, model, request, redirectAttributes);
+ assertEquals("Expected email errors", "email.invalid", results.getFieldError().getCode());
+ // does not exists
+ results = new DirectFieldBindingResult(newUser, ModelKeys.NEW_USER);
+ newUser.setEmail("test@mail.com");
+ String viewResult = controller.requestUsername(newUser, results, model, request, redirectAttributes);
+ assertEquals("Expected email errors", 1, results.getErrorCount());
+ assertEquals("Expected email errors", "email.doesnot.exist", results.getFieldError().getCode());
+ assertThat(viewResult, CoreMatchers.equalTo(ViewNames.USERNAME_REQUEST));
+ // password part:
+ model = createNiceMock(Model.class);
+ newUser = new NewUser();
+ results = new DirectFieldBindingResult(newUser, ModelKeys.NEW_USER);
+ redirectAttributes = createNiceMock(RedirectAttributes.class);
+ replay(redirectAttributes);
+ replay(model);
+ // required email
+ controller.requestPassword(newUser, results, model, request, redirectAttributes);
+ assertThat(captchaService.isValid(request), CoreMatchers.equalTo(true));
+ assertEquals("Expected email errors", 1, results.getErrorCount());
+ assertEquals("Expected email errors", "email.required", results.getFieldError().getCode());
+ // invalid email
+ results = new DirectFieldBindingResult(newUser, ModelKeys.NEW_USER);
+ newUser.setEmail("test_email");
+ controller.requestPassword(newUser, results, model, request, redirectAttributes);
+ assertEquals("Expected email errors", "email.invalid", results.getFieldError().getCode());
+ // does not exists
+ results = new DirectFieldBindingResult(newUser, ModelKeys.NEW_USER);
+ newUser.setEmail("test@mail.com");
+ viewResult = controller.requestPassword(newUser, results, model, request, redirectAttributes);
+ assertEquals("Expected email errors", 1, results.getErrorCount());
+ assertEquals("Expected email errors", "email.doesnot.exist", results.getFieldError().getCode());
+ assertThat(viewResult, CoreMatchers.equalTo(ViewNames.NEW_PASSWORD_REQUEST));
+
+ }
+}
Modified: incubator/rave/trunk/rave-portal-resources/src/main/resources/messages.properties
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-portal-resources/src/main/resources/messages.properties?rev=1292365&r1=1292364&r2=1292365&view=diff
==============================================================================
--- incubator/rave/trunk/rave-portal-resources/src/main/resources/messages.properties (original)
+++ incubator/rave/trunk/rave-portal-resources/src/main/resources/messages.properties Wed Feb 22 16:05:57 2012
@@ -32,11 +32,16 @@ confirmPassword.mismatch=Password mismat
email.required=Email required
email.invalid=This is not a valid email address
email.exists=This email address already exists for a user
-
+email.doesnot.exist=This email address doesn't exists
+account.invalid=Invalid account. Please contact administrator.
form.some.fields.required=Field marked with * are required
form.all.fields.required=All fields are required.
form.field.error.required=This field is required
+page.changepassword.button=Change password
+page.changepassword.title=Change password
+page.changepassword.expired=Request to change your password has been expired. Please request another password reminder.
+
page.error.title=Rave has suffered a brief meltdown
page.error.message=Please bear with us while we fetch some ice cubes. In the meantime please try
page.error.reload=reloading
@@ -103,10 +108,25 @@ page.login.rememberme=Remember me
page.login.usernamepassword=Username and Password
page.login.usernamepassword.fail=The username or password is incorrect.
page.login.usernamepassword.login=Login
+page.login.forgot.password=Forgot password
+page.login.forgot.password.label=Password reminder
+page.login.forgot.password.button=Request new password
+page.login.forgot.username=Username reminder
+page.login.forgot.username.label=Username reminder
+page.login.forgot.username.button=Request username
page.newaccount.title=New Account Application
page.newaccount.button=Create Account
+page.newpassword.title=Request new password
+page.newpassword.email.sent=An email has been sent to {0}.
+page.newpassword.email.sent.login=Click here to login
+page.newpassword.password.button=Request new password
+page.newpassword.password.title=Request new password
+page.newpassword.username.title=Request username
+
+page.retrieveusername.title=Request your username
+
page.store.title=Widget Store
page.store.search=Search in widget store
page.store.search.button=Search
@@ -137,6 +157,8 @@ page.widget.rate.likes=Likes:
page.widget.rate.dislikes=Dislikes:
page.widget.tags.title=Tags:
page.widget.tags.add=Add New Tag:
+page.widget.tags.or=--or--
+page.widget.tags.select=Select from list:
page.addwidget.title=Add new widget
page.addwidget.form.header=Widget
Modified: incubator/rave/trunk/rave-portal-resources/src/main/resources/messages_nl.properties
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-portal-resources/src/main/resources/messages_nl.properties?rev=1292365&r1=1292364&r2=1292365&view=diff
==============================================================================
--- incubator/rave/trunk/rave-portal-resources/src/main/resources/messages_nl.properties (original)
+++ incubator/rave/trunk/rave-portal-resources/src/main/resources/messages_nl.properties Wed Feb 22 16:05:57 2012
@@ -32,6 +32,13 @@ confirmPassword.mismatch=Wachtwoord komt
email.required=Email verplicht
email.invalid=Dit is geen geldig email adres
email.exists=Dit email adres bestaat al voor een gebruiker
+email.doesnot.exist=Dit email adres bestaat niet
+account.invalid=Invalid account. Please contact administrator.
+
+page.changepassword.button=Verander password
+page.changepassword.title=Verander password
+page.changepassword.expired=Request to change your password is either invalid or has been expired. Please request another password reminder.
+
form.some.fields.required=Velden met een * zijn verplicht
form.all.fields.required=Alle velden zijn verplicht
@@ -112,10 +119,25 @@ page.login.rememberme=Onthoud mij
page.login.usernamepassword=Gebruikersnaam en wachtwoord
page.login.usernamepassword.fail=De gebruikersnaam of het wachtwoord is incorrect.
page.login.usernamepassword.login=Inloggen
+page.login.forgot.password=Password reminder
+page.login.forgot.password.label=Password reminder
+page.login.forgot.password.button=Request new password
+page.login.forgot.username=Username reminder
+page.login.forgot.username.label=Username reminder
+page.login.forgot.username.button=Request username
page.newaccount.title=Nieuw account applicatie
page.newaccount.button=Cre\u00EBer account
+page.newpassword.title=Request new password
+page.newpassword.email.sent=An email has been sent to {0}.
+page.newpassword.email.sent.login=Click here to login
+page.newpassword.password.button=Request new password
+page.newpassword.password.title=Request new password
+page.newpassword.username.title=Request username
+
+page.retrieveusername.title=Request your username
+
page.store.title=Widgetwinkel
page.store.search=Zoek in de widgetwinkel
page.store.search.button=Zoek
Modified: incubator/rave/trunk/rave-portal-resources/src/main/resources/portal.properties
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-portal-resources/src/main/resources/portal.properties?rev=1292365&r1=1292364&r2=1292365&view=diff
==============================================================================
--- incubator/rave/trunk/rave-portal-resources/src/main/resources/portal.properties (original)
+++ incubator/rave/trunk/rave-portal-resources/src/main/resources/portal.properties Wed Feb 22 16:05:57 2012
@@ -57,4 +57,19 @@ portal.captcha.enabled=false
portal.captcha.key.private=
portal.captcha.key.public=
portal.captcha.usenoscript=false
-portal.captcha.invalid.configuration=<label class="error">ReCaptcha service is not properly configured.</label>
\ No newline at end of file
+portal.captcha.invalid.configuration=<label class="error">ReCaptcha service is not properly configured.</label>
+
+#mail settings
+portal.mail.sender=m@mitre.org
+portal.mail.replyto=mfranklin@mitre.org
+portal.mail.host=smtp-bedford.mitre.org
+portal.mail.password=
+portal.mail.username=
+portal.mail.protocol=smtp
+portal.mail.port=25
+portal.mail.username.subject=Rave username reminder service
+portal.mail.username.template=username_reminder.ftl
+portal.mail.passwordservice.subject=Rave password reminder service
+portal.mail.passwordservice.template=password_reminder.ftl
+portal.mail.passwordservice.valid.minutes=30
+portal.mail.service.baseurl=http://localhost:8080/portal/app/changepassword/
\ No newline at end of file