diff --git a/etc/venice-config.xml b/etc/venice-config.xml index 06b9a35..0019335 100644 --- a/etc/venice-config.xml +++ b/etc/venice-config.xml @@ -125,7 +125,15 @@ Here is the password reminder for your account "${username}" as you requested: ${reminder} If this reminder is not sufficient for you to remember what your password is, -please contact the server administrator for further assistance. +then the system can change your password for you. To do so, please visit the following URL: + +http://delenn/venice/passrecovery/${change.uid}.${change.auth} + +Your password will be changed and a new password will be E-mailed to you at this address. + +If you did NOT request a password reminder, then this message was sent by someone +attempting to access your account without your knowledge. Do not panic! Nothing has +happened to your account or password yet, but please do notify the system administrator. -- The Management ]]> @@ -133,6 +141,22 @@ please contact the server administrator for further assistance. Venice Password Reminder Message + + + + + Venice Password Changed + @@ -140,7 +164,7 @@ please contact the server administrator for further assistance. and click the "Create Account" link +your Web browser at and click the "Create Account" link at the top of the page, or click the "Log In" link if you already have a Venice account. Once you have completed the process, click the "Join Now" button. You will then be able to take part in the conferences that are going on in the SIG. @@ -161,7 +185,7 @@ Hope to see you in "${signame}" soon! and click the "Create Account" link +your Web browser at and click the "Create Account" link at the top of the page, or click the "Log In" link if you already have a Venice account. Once you have completed the process, click the "Join Now" button. You will be prompted for the "password" for this SIG, which is "${joinkey}". You will then be able to take part in the conferences that are going on in the diff --git a/etc/web.xml b/etc/web.xml index 2c59ed8..2d190fe 100644 --- a/etc/web.xml +++ b/etc/web.xml @@ -230,6 +230,14 @@ com.silverwrist.venice.servlets.StyleSheet + + passrecovery + + Performs a password change operation for a user that's forgotten their password. + + com.silverwrist.venice.servlets.PasswordRecovery + + @@ -348,6 +356,11 @@ /stylesheet + + passrecovery + /passrecovery/* + + testformdata diff --git a/src/com/silverwrist/venice/core/VeniceEngine.java b/src/com/silverwrist/venice/core/VeniceEngine.java index 96551cb..47d49a9 100644 --- a/src/com/silverwrist/venice/core/VeniceEngine.java +++ b/src/com/silverwrist/venice/core/VeniceEngine.java @@ -44,6 +44,9 @@ public interface VeniceEngine extends SearchMode public abstract void sendPasswordReminder(String username) throws DataException, AccessError, EmailException; + public abstract void completePasswordChange(int uid, int authentication) + throws DataException, AccessError, EmailException; + public abstract UserContext createNewAccount(String remote_addr, String username, String password, String reminder) throws DataException, AccessError; diff --git a/src/com/silverwrist/venice/core/impl/VeniceEngineImpl.java b/src/com/silverwrist/venice/core/impl/VeniceEngineImpl.java index 735724e..3929444 100644 --- a/src/com/silverwrist/venice/core/impl/VeniceEngineImpl.java +++ b/src/com/silverwrist/venice/core/impl/VeniceEngineImpl.java @@ -31,6 +31,7 @@ import com.silverwrist.venice.htmlcheck.*; import com.silverwrist.venice.htmlcheck.dict.*; import com.silverwrist.venice.htmlcheck.filters.*; import com.silverwrist.venice.security.AuditRecord; +import com.silverwrist.venice.security.PasswordGenerator; import com.silverwrist.venice.security.PasswordHash; import com.silverwrist.venice.security.DefaultLevels; @@ -408,6 +409,64 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend } // end class ConferenceCoreDataCreator + /*-------------------------------------------------------------------------------- + * Internal class for password change requests + *-------------------------------------------------------------------------------- + */ + + class PasswordChangeRequest + { + private static final long EXPIRE_TIME = 3600000L; // one hour + + private int uid; // UID of the password change request + private String username; // username of the password change request + private int authentication; // authentication value + private String email; // email address + private long timestamp; // current timestamp + + PasswordChangeRequest(int uid, String username, String email) + { + this.uid = uid; + this.username = username; + this.email = email; + this.authentication = rng.nextInt(Integer.MAX_VALUE); + this.timestamp = System.currentTimeMillis(); + + } // end constructor + + final int getUID() + { + return uid; + + } // end getUID + + final String getUserName() + { + return username; + + } // end getUserName + + final int getAuthentication() + { + return authentication; + + } // end getAuthentication + + final String getEmail() + { + return email; + + } // end getEmail + + final boolean isExpired() + { + long diff = System.currentTimeMillis() - timestamp; + return (diff>EXPIRE_TIME); + + } // end isExpired + + } // end class PasswordChangeRequest + /*-------------------------------------------------------------------------------- * Static data values *-------------------------------------------------------------------------------- @@ -445,6 +504,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend private LinkedList cache_fp_posts = new LinkedList(); // all posts that have been published to front page private boolean cache_fp_posts_busy = false; // busy flag for above vector private HashSet no_compress_types = new HashSet(); // the file types that can't be compressed + private HashMap password_changes = new HashMap(); // current password change requests /*-------------------------------------------------------------------------------- * Constructor @@ -1025,8 +1085,8 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend { // look for a user name matching this user record conn = datapool.getConnection(); Statement stmt = conn.createStatement(); - StringBuffer sql = new StringBuffer("SELECT c.email, u.passreminder FROM users u, contacts c WHERE " - + "u.contactid = c.contactid AND u.username = '"); + StringBuffer sql = new StringBuffer("SELECT c.email, u.uid, u.passreminder FROM users u, contacts c " + + "WHERE u.contactid = c.contactid AND u.username = '"); sql.append(SQLUtil.encodeString(username)).append("';"); ResultSet rs = stmt.executeQuery(sql.toString()); if (!(rs.next())) @@ -1041,6 +1101,10 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend if (logger.isDebugEnabled()) logger.debug("sendPasswordReminder(\"" + username + "\") going out to \"" + email_addr + "\""); + // Enqueue a password change request. + PasswordChangeRequest pcr = new PasswordChangeRequest(rs.getInt(2),username,email_addr); + password_changes.put(new Integer(pcr.getUID()),pcr); + // Create the message to be sent. String message = getStockMessage("reminder"); if (message==null) @@ -1051,9 +1115,11 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend } // end if // Replace the message variables. - HashMap vars = new HashMap(2); + HashMap vars = new HashMap(4); vars.put("username",username); - vars.put("reminder",rs.getString(2)); + vars.put("reminder",rs.getString(3)); + vars.put("change.uid",String.valueOf(pcr.getUID())); + vars.put("change.auth",String.valueOf(pcr.getAuthentication())); message = StringUtil.replaceAllVariables(message,vars); // Find the message subject. @@ -1086,6 +1152,86 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend } // end sendPasswordReminder + public void completePasswordChange(int uid, int authentication) + throws DataException, AccessError, EmailException + { + checkInitialized(); + Integer key = new Integer(uid); + PasswordChangeRequest pcr; + synchronized (this) + { // make sure we can't pull the same request out twice! + pcr = (PasswordChangeRequest)(password_changes.get(key)); + if (pcr==null) + throw new AccessError("Password change request not found."); + password_changes.remove(key); // can't use the same request twice! + + } // end synchronized block + + if (authentication!=pcr.getAuthentication()) + throw new AccessError("Invalid password change request."); + if (pcr.isExpired()) + throw new AccessError("Password change request has expired."); + + if (logger.isDebugEnabled()) + logger.debug("completePasswordChange for user \"" + pcr.getUserName() + "\""); + + // Request is valid; synthesize a new password + PasswordGenerator pgen = new PasswordGenerator(); + PasswordHash phash = new PasswordHash(pgen.toString()); + Connection conn = null; + try + { // perform the database update + conn = datapool.getConnection(); + Statement stmt = conn.createStatement(); + StringBuffer sql = new StringBuffer("UPDATE users SET passhash = '"); + sql.append(phash.toString()).append("' WHERE uid = ").append(uid).append(';'); + stmt.executeUpdate(sql.toString()); + + } // end try + catch (SQLException e) + { // database error - this is a DataException + logger.error("DB error resetting password for user: " + e.getMessage(),e); + throw new DataException("unable to reset password: " + e.getMessage(),e); + + } // end catch + finally + { // make sure the connection is released before we go + if (conn!=null) + datapool.releaseConnection(conn); + + } // end finally + + // Create the message to be sent. + String message = getStockMessage("password-change"); + if (message==null) + { // what? the message does not exist? + logger.error("\"password-change\" message does not exist in config file!"); + throw new DataException("INTERNAL: password change message not specified"); + + } // end if + + // Replace the message variables. + HashMap vars = new HashMap(2); + vars.put("username",pcr.getUserName()); + vars.put("password",pgen.toString()); + message = StringUtil.replaceAllVariables(message,vars); + + // Find the message subject. + String subject = getStockMessage("password-change-subject"); + if (subject==null) + subject = "Venice Password Changed"; + + // Create the emailer and send the message. + SimpleEmailer emailer = createEmailer(); + emailer.setTo(pcr.getEmail()); + emailer.setSubject(subject); + emailer.setText(message); + emailer.send(); + if (logger.isDebugEnabled()) + logger.debug("...email sent"); + + } // end completePasswordChange + public UserContext createNewAccount(String remote_addr, String username, String password, String reminder) throws DataException, AccessError { diff --git a/src/com/silverwrist/venice/servlets/PasswordRecovery.java b/src/com/silverwrist/venice/servlets/PasswordRecovery.java new file mode 100644 index 0000000..90e5ca3 --- /dev/null +++ b/src/com/silverwrist/venice/servlets/PasswordRecovery.java @@ -0,0 +1,103 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at . + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Communities System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.venice.servlets; + +import java.io.*; +import javax.servlet.*; +import javax.servlet.http.*; +import org.apache.log4j.*; +import com.silverwrist.venice.core.*; +import com.silverwrist.venice.servlets.format.*; + +public class PasswordRecovery extends VeniceServlet +{ + /*-------------------------------------------------------------------------------- + * Static data members + *-------------------------------------------------------------------------------- + */ + + private static Category logger = Category.getInstance(PasswordRecovery.class); + + /*-------------------------------------------------------------------------------- + * Overrides from class HttpServlet + *-------------------------------------------------------------------------------- + */ + + public String getServletInfo() + { + String rc = "PasswordRecovery servlet - Chnages passwords for users that forgot them\n" + + "Part of the Venice Web Communities System\n"; + return rc; + + } // end getServletInfo + + /*-------------------------------------------------------------------------------- + * Overrides from class VeniceServlet + *-------------------------------------------------------------------------------- + */ + + protected VeniceContent doVeniceGet(HttpServletRequest request, VeniceEngine engine, + UserContext user, RenderData rdat) + throws ServletException, IOException, VeniceServletResult + { + int uid, auth; + try + { // retrieve UID and authentication strings from URL + String foo = request.getPathInfo().substring(1); + int n = foo.indexOf('.'); + if (n<0) + return new ErrorBox(null,"Invalid parameters to password recovery.","top"); + uid = Integer.parseInt(foo.substring(0,n)); + auth = Integer.parseInt(foo.substring(n+1)); + + } // end try + catch (NumberFormatException nfe) + { // invalid parameters passed... + return new ErrorBox(null,"Invalid parameters to password recovery.","top"); + + } // end catch + + try + { // complete the password change + engine.completePasswordChange(uid,auth); + + // now return a "password changed" page + changeMenuTop(request); + setMyLocation(request,"top"); // lie so that we get the "Log In" link up top + return new PasswordChanged(); + + } // end try + catch (DataException de) + { // there was a database error changing your password + return new ErrorBox("Database Error","Database error changing password: " + de.getMessage(),"top"); + + } // end catch + catch (AccessError ae) + { // this indicates a problem with the request ID or authentication + return new ErrorBox("Invalid Request",ae.getMessage(),"top"); + + } // end catch + catch (EmailException ee) + { // error sending the confirmation email + return new ErrorBox("E-mail Error","E-mail error sending update: " + ee.getMessage(),"top"); + + } // end catch + + } // end doVeniceGet + +} // end class PasswordRecovery diff --git a/src/com/silverwrist/venice/servlets/format/PasswordChanged.java b/src/com/silverwrist/venice/servlets/format/PasswordChanged.java new file mode 100644 index 0000000..caac8de --- /dev/null +++ b/src/com/silverwrist/venice/servlets/format/PasswordChanged.java @@ -0,0 +1,87 @@ +/* + * The contents of this file are subject to the Mozilla Public License Version 1.1 + * (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at . + * + * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + * WARRANTY OF ANY KIND, either express or implied. See the License for the specific + * language governing rights and limitations under the License. + * + * The Original Code is the Venice Web Communities System. + * + * The Initial Developer of the Original Code is Eric J. Bowersox , + * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + * + * Contributor(s): + */ +package com.silverwrist.venice.servlets.format; + +import javax.servlet.ServletRequest; +import com.silverwrist.venice.core.SIGContext; + +public class PasswordChanged implements JSPRender +{ + /*-------------------------------------------------------------------------------- + * Static data members + *-------------------------------------------------------------------------------- + */ + + // Attribute name for request attribute + protected static final String ATTR_NAME = "com.silverwrist.venice.content.PasswordChanged"; + + /*-------------------------------------------------------------------------------- + * Constructor + *-------------------------------------------------------------------------------- + */ + + public PasswordChanged() + { // do nothing + } // end constructor + + /*-------------------------------------------------------------------------------- + * External static functions + *-------------------------------------------------------------------------------- + */ + + public static PasswordChanged retrieve(ServletRequest request) + { + return (PasswordChanged)(request.getAttribute(ATTR_NAME)); + + } // end retrieve + + /*-------------------------------------------------------------------------------- + * Implementations from interface VeniceContent + *-------------------------------------------------------------------------------- + */ + + public String getPageTitle(RenderData rdat) + { + return "Your Password Has been Changed"; + + } // end getPageTitle + + public String getPageQID() + { + return null; + + } // end getPageQID + + /*-------------------------------------------------------------------------------- + * Implementations from interface JSPRender + *-------------------------------------------------------------------------------- + */ + + public void store(ServletRequest request) + { + request.setAttribute(ATTR_NAME,this); + + } // end store + + public String getTargetJSPName() + { + return "password_changed.jsp"; + + } // end getTargetJSPName + +} // end class PasswordChanged diff --git a/src/com/silverwrist/venice/servlets/format/SIGWelcome.java b/src/com/silverwrist/venice/servlets/format/SIGWelcome.java index 0f8e5a4..298f7f7 100644 --- a/src/com/silverwrist/venice/servlets/format/SIGWelcome.java +++ b/src/com/silverwrist/venice/servlets/format/SIGWelcome.java @@ -7,7 +7,7 @@ * WARRANTY OF ANY KIND, either express or implied. See the License for the specific * language governing rights and limitations under the License. * - * The Original Code is the Venice Web Community System. + * The Original Code is the Venice Web Communities System. * * The Initial Developer of the Original Code is Eric J. Bowersox , * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are diff --git a/web/format/password_changed.jsp b/web/format/password_changed.jsp new file mode 100644 index 0000000..2c4ce64 --- /dev/null +++ b/web/format/password_changed.jsp @@ -0,0 +1,31 @@ +<%-- + The contents of this file are subject to the Mozilla Public License Version 1.1 + (the "License"); you may not use this file except in compliance with the License. + You may obtain a copy of the License at . + + Software distributed under the License is distributed on an "AS IS" basis, WITHOUT + WARRANTY OF ANY KIND, either express or implied. See the License for the specific + language governing rights and limitations under the License. + + The Original Code is the Venice Web Communities System. + + The Initial Developer of the Original Code is Eric J. Bowersox , + for Silverwrist Design Studios. Portions created by Eric J. Bowersox are + Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. + + Contributor(s): +--%> +<%@ page import = "com.silverwrist.venice.servlets.format.*" %> +<% + RenderData rdat = RenderConfig.createRenderData(application,request,response); +%> +<% if (rdat.useHTMLComments()) { %><% } %> +<% rdat.writeContentHeader(out,"Your Password Has Been Changed",null); %> +<%= rdat.getStdFontTag(ColorSelectors.CONTENT_FOREGROUND,2) %> + The password for your account has been changed, and a new password has been E-mailed to you at + your account's defined E-mail address. (Check your E-mail again!). After getting the new password, + please log in and change your password (yes, again!) as soon as possible.

+

+ \ No newline at end of file diff --git a/web/format/sigwelcome.jsp b/web/format/sigwelcome.jsp index 4c81d03..edd6469 100644 --- a/web/format/sigwelcome.jsp +++ b/web/format/sigwelcome.jsp @@ -7,7 +7,7 @@ WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. - The Original Code is the Venice Web Community System. + The Original Code is the Venice Web Communities System. The Initial Developer of the Original Code is Eric J. Bowersox , for Silverwrist Design Studios. Portions created by Eric J. Bowersox are