001: /*
002: * AccountManager.java
003: *
004: * Version: $Revision: 1962 $
005: *
006: * Date: $Date: 2007-05-23 13:36:35 -0500 (Wed, 23 May 2007) $
007: *
008: * Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts
009: * Institute of Technology. All rights reserved.
010: *
011: * Redistribution and use in source and binary forms, with or without
012: * modification, are permitted provided that the following conditions are
013: * met:
014: *
015: * - Redistributions of source code must retain the above copyright
016: * notice, this list of conditions and the following disclaimer.
017: *
018: * - Redistributions in binary form must reproduce the above copyright
019: * notice, this list of conditions and the following disclaimer in the
020: * documentation and/or other materials provided with the distribution.
021: *
022: * - Neither the name of the Hewlett-Packard Company nor the name of the
023: * Massachusetts Institute of Technology nor the names of their
024: * contributors may be used to endorse or promote products derived from
025: * this software without specific prior written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
028: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
029: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
030: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
031: * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
032: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
033: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
034: * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
035: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
036: * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
037: * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
038: * DAMAGE.
039: */
040: package org.dspace.eperson;
041:
042: import java.io.IOException;
043: import java.sql.SQLException;
044: import java.sql.Timestamp;
045: import java.util.Calendar;
046: import java.util.Locale;
047:
048: import javax.mail.MessagingException;
049:
050: import org.apache.log4j.Logger;
051: import org.dspace.authorize.AuthorizeException;
052: import org.dspace.core.ConfigurationManager;
053: import org.dspace.core.Context;
054: import org.dspace.core.Email;
055: import org.dspace.core.I18nUtil;
056: import org.dspace.core.Utils;
057: import org.dspace.storage.rdbms.DatabaseManager;
058: import org.dspace.storage.rdbms.TableRow;
059:
060: /**
061: * Methods for handling registration by email and forgotten passwords. When
062: * someone registers as a user, or forgets their password, the
063: * sendRegistrationInfo or sendForgotPasswordInfo methods can be used to send an
064: * email to the user. The email contains a special token, a long string which is
065: * randomly generated and thus hard to guess. When the user presents the token
066: * back to the system, the AccountManager can use the token to determine the
067: * identity of the eperson.
068: *
069: * *NEW* now ignores expiration dates so that tokens never expire
070: *
071: * @author Peter Breton
072: * @version $Revision: 1962 $
073: */
074: public class AccountManager {
075: /** log4j log */
076: private static Logger log = Logger.getLogger(AccountManager.class);
077:
078: /** Protected Constructor */
079: protected AccountManager() {
080: }
081:
082: /**
083: * Email registration info to the given email address.
084: *
085: * Potential error conditions: Cannot create registration data in database
086: * (throws SQLException) Error sending email (throws MessagingException)
087: * Error reading email template (throws IOException) Authorization error
088: * (throws AuthorizeException)
089: *
090: * @param context
091: * DSpace context
092: * @param email
093: * Email address to send the registration email to
094: */
095: public static void sendRegistrationInfo(Context context,
096: String email) throws SQLException, IOException,
097: MessagingException, AuthorizeException {
098: sendInfo(context, email, true, true);
099: }
100:
101: /**
102: * Email forgot password info to the given email address.
103: *
104: * Potential error conditions: No EPerson with that email (returns null)
105: * Cannot create registration data in database (throws SQLException) Error
106: * sending email (throws MessagingException) Error reading email template
107: * (throws IOException) Authorization error (throws AuthorizeException)
108: *
109: * @param context
110: * DSpace context
111: * @param email
112: * Email address to send the forgot-password email to
113: */
114: public static void sendForgotPasswordInfo(Context context,
115: String email) throws SQLException, IOException,
116: MessagingException, AuthorizeException {
117: sendInfo(context, email, false, true);
118: }
119:
120: /**
121: * <p>
122: * Return the EPerson corresponding to token, where token was emailed to the
123: * person by either the sendRegistrationInfo or sendForgotPasswordInfo
124: * methods.
125: * </p>
126: *
127: * <p>
128: * If the token is not found return null.
129: * </p>
130: *
131: * @param context
132: * DSpace context
133: * @param token
134: * Account token
135: * @return The EPerson corresponding to token, or null.
136: * @exception SQLException
137: * If the token or eperson cannot be retrieved from the
138: * database.
139: */
140: public static EPerson getEPerson(Context context, String token)
141: throws SQLException, AuthorizeException {
142: String email = getEmail(context, token);
143:
144: if (email == null) {
145: return null;
146: }
147:
148: EPerson ep = EPerson.findByEmail(context, email);
149:
150: return ep;
151: }
152:
153: /**
154: * Return the e-mail address referred to by a token, or null if email
155: * address can't be found ignores expiration of token
156: *
157: * @param context
158: * DSpace context
159: * @param token
160: * Account token
161: * @return The email address corresponding to token, or null.
162: */
163: public static String getEmail(Context context, String token)
164: throws SQLException {
165: TableRow rd = DatabaseManager.findByUnique(context,
166: "RegistrationData", "token", token);
167:
168: if (rd == null) {
169: return null;
170: }
171:
172: /*
173: * ignore the expiration date on tokens Date expires =
174: * rd.getDateColumn("expires"); if (expires != null) { if ((new
175: * java.util.Date()).after(expires)) return null; }
176: */
177: return rd.getStringColumn("email");
178: }
179:
180: /**
181: * Delete token.
182: *
183: * @param context
184: * DSpace context
185: * @param token
186: * The token to delete
187: * @exception SQLException
188: * If a database error occurs
189: */
190: public static void deleteToken(Context context, String token)
191: throws SQLException {
192: DatabaseManager.deleteByValue(context, "RegistrationData",
193: "token", token);
194: }
195:
196: /*
197: * THIS IS AN INTERNAL METHOD. THE SEND PARAMETER ALLOWS IT TO BE USED FOR
198: * TESTING PURPOSES.
199: *
200: * Send an info to the EPerson with the given email address. If isRegister
201: * is TRUE, this is registration email; otherwise, it is forgot-password
202: * email. If send is TRUE, the email is sent; otherwise it is skipped.
203: *
204: * Potential error conditions: No EPerson with that email (returns null)
205: * Cannot create registration data in database (throws SQLException) Error
206: * sending email (throws MessagingException) Error reading email template
207: * (throws IOException) Authorization error (throws AuthorizeException)
208: *
209: * @param context DSpace context @param email Email address to send the
210: * forgot-password email to @param isRegister If true, this is for
211: * registration; otherwise, it is for forgot-password @param send If true,
212: * send email; otherwise do not send any email
213: */
214: protected static TableRow sendInfo(Context context, String email,
215: boolean isRegister, boolean send) throws SQLException,
216: IOException, MessagingException, AuthorizeException {
217: // See if a registration token already exists for this user
218: TableRow rd = DatabaseManager.findByUnique(context,
219: "registrationdata", "email", email);
220:
221: // If it already exists, just re-issue it
222: if (rd == null) {
223: rd = DatabaseManager.create(context, "RegistrationData");
224: rd.setColumn("token", Utils.generateHexKey());
225:
226: // don't set expiration date any more
227: // rd.setColumn("expires", getDefaultExpirationDate());
228: rd.setColumn("email", email);
229: DatabaseManager.update(context, rd);
230:
231: // This is a potential problem -- if we create the callback
232: // and then crash, registration will get SNAFU-ed.
233: // So FIRST leave some breadcrumbs
234: if (log.isDebugEnabled()) {
235: log.debug("Created callback "
236: + rd.getIntColumn("registrationdata_id")
237: + " with token " + rd.getStringColumn("token")
238: + " with email \"" + email + "\"");
239: }
240: }
241:
242: if (send) {
243: sendEmail(context, email, isRegister, rd);
244: }
245:
246: return rd;
247: }
248:
249: /**
250: * Send a DSpace message to the given email address.
251: *
252: * If isRegister is <code>true</code>, this is registration email;
253: * otherwise, it is a forgot-password email.
254: *
255: * @param email
256: * The email address to mail to
257: * @param isRegister
258: * If true, this is registration email; otherwise it is
259: * forgot-password email.
260: * @param rd
261: * The RDBMS row representing the registration data.
262: * @exception MessagingException
263: * If an error occurs while sending email
264: * @exception IOException
265: * If an error occurs while reading the email template.
266: */
267: private static void sendEmail(Context context, String email,
268: boolean isRegister, TableRow rd) throws MessagingException,
269: IOException, SQLException {
270: String base = ConfigurationManager.getProperty("dspace.url");
271:
272: // Note change from "key=" to "token="
273: String specialLink = new StringBuffer().append(base).append(
274: base.endsWith("/") ? "" : "/").append(
275: isRegister ? "register" : "forgot").append("?").append(
276: "token=").append(rd.getStringColumn("token"))
277: .toString();
278: Locale locale = context.getCurrentLocale();
279: Email bean = ConfigurationManager.getEmail(I18nUtil
280: .getEmailFilename(locale, isRegister ? "register"
281: : "change_password"));
282: bean.addRecipient(email);
283: bean.addArgument(specialLink);
284: bean.send();
285:
286: // Breadcrumbs
287: if (log.isInfoEnabled()) {
288: log.info("Sent "
289: + (isRegister ? "registration" : "account")
290: + " information to " + email);
291: }
292: }
293:
294: /**
295: * Return the date on which registrations expire.
296: *
297: * @return - The date on which registrations expire
298: */
299: private static Timestamp getDefaultExpirationDate() {
300: Calendar calendar = Calendar.getInstance();
301:
302: calendar.setTime(new java.util.Date());
303:
304: // Add 1 year from today
305: calendar.add(Calendar.WEEK_OF_YEAR, 52);
306:
307: return new java.sql.Timestamp(calendar.getTime().getTime());
308: }
309: }
|