001: /**
002: * Copyright (c) 2003-2007, David A. Czarnecki
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions are met:
007: *
008: * Redistributions of source code must retain the above copyright notice, this list of conditions and the
009: * following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
011: * following disclaimer in the documentation and/or other materials provided with the distribution.
012: * Neither the name of "David A. Czarnecki" and "blojsom" nor the names of its contributors may be used to
013: * endorse or promote products derived from this software without specific prior written permission.
014: * Products derived from this software may not be called "blojsom", nor may "blojsom" appear in their name,
015: * without prior written permission of David A. Czarnecki.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
018: * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
019: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
021: * EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
022: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
025: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
026: * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
027: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
028: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
029: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
030: */package org.blojsom.plugin.registration;
031:
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034: import org.apache.commons.mail.Email;
035: import org.apache.commons.mail.EmailException;
036: import org.apache.commons.mail.HtmlEmail;
037: import org.blojsom.blog.Blog;
038: import org.blojsom.blog.Entry;
039: import org.blojsom.blog.User;
040: import org.blojsom.blog.database.DatabaseUser;
041: import org.blojsom.fetcher.Fetcher;
042: import org.blojsom.fetcher.FetcherException;
043: import org.blojsom.plugin.PluginException;
044: import org.blojsom.plugin.admin.BaseAdminPlugin;
045: import org.blojsom.plugin.notification.Notification;
046: import org.blojsom.util.BlojsomUtils;
047: import org.blojsom.util.resources.ResourceManager;
048:
049: import javax.mail.MessagingException;
050: import javax.mail.Session;
051: import javax.naming.Context;
052: import javax.naming.InitialContext;
053: import javax.naming.NamingException;
054: import javax.servlet.http.HttpServletRequest;
055: import javax.servlet.http.HttpServletResponse;
056: import java.net.MalformedURLException;
057: import java.util.*;
058:
059: /**
060: * This plugin allows users to register for an account on the blog. Can be
061: * combined with various authorization plugins to force users to login before
062: * they can access the blog.
063: *
064: * @author Eric Broyles
065: * @version $Id: RegistrationPlugin.java,v 1.2 2007/01/17 02:35:05 czarneckid Exp $
066: */
067: public class RegistrationPlugin extends BaseAdminPlugin {
068: private Log _logger;
069:
070: private static final String REGISTRATION_USERNAME_PARAM = "username";
071:
072: private static final String REGISTRATION_FULLNAME_PARAM = "fullname";
073:
074: private static final String REGISTRATION_EMAIL_PARAM = "email";
075:
076: private static final String REGISTRATION_PAGE = "registration";
077:
078: private static final String FAILED_REGISTRATION_KEY = "failed.registration.text";
079:
080: private static final String CONSTRUCTED_REGISTRATION_EMAIL_KEY = "constructed.registration.email.text";
081:
082: private static final String USERNAME_BLANK_KEY = "username.blank.text";
083:
084: private static final String REGISTRATION_ACTIVATION_SUCCESS_KEY = "registration.activation.successful.text";
085:
086: private static final String REGISTRATION_ACTIVATION_FAILED_KEY = "registration.activation.failed.text";
087:
088: private static final String REGISTRATION_ACTIVATION_ACTION = "activate";
089:
090: private static final String REGISTERED_STATUS = "registered";
091:
092: private static final String APPROVED_STATUS = "approved";
093:
094: private static final String REGISTRATION_USER_REQUIRED_METADATA_KEYS = "registration-user-required-metadata-keys";
095:
096: private static final String REGISTRATION_USER_OPTIONAL_METADATA_KEYS = "registration-user-optional-metadata-keys";
097:
098: private static final String REGISTRATION_MISSING_REQUIRED_VALUE_KEY = "registration.missing.required.value.text";
099:
100: protected static final String REGISTRATION_MESSAGES_RESOURCE = "org.blojsom.plugin.registration.messages";
101:
102: private Fetcher _fetcher;
103:
104: private String _mailServer;
105:
106: private String _mailServerUsername;
107:
108: private String _mailServerPassword;
109:
110: private Session _session;
111:
112: /**
113: *
114: */
115: public RegistrationPlugin() {
116: _logger = LogFactory.getLog(RegistrationPlugin.class);
117: }
118:
119: /**
120: * Set the Fetcher. Usually injected by Spring.
121: *
122: * @param fetcher {@link Fetcher}
123: */
124: public void setFetcher(Fetcher fetcher) {
125: _fetcher = fetcher;
126: }
127:
128: /**
129: * @see org.blojsom.plugin.admin.BaseAdminPlugin#init()
130: */
131: public void init() throws PluginException {
132: super .init();
133: _mailServer = _servletConfig.getServletContext()
134: .getInitParameter("smtp-server");
135: if (_mailServer != null) {
136: if (_mailServer.startsWith("java:comp/env")) {
137: try {
138: Context context = new InitialContext();
139: _session = (Session) context.lookup(_mailServer);
140: } catch (NamingException e) {
141: if (_logger.isErrorEnabled())
142: _logger.error(e);
143: throw new PluginException(e);
144: }
145: } else {
146: _mailServerUsername = _servletConfig
147: .getServletContext().getInitParameter(
148: "smtp-server-username");
149: _mailServerPassword = _servletConfig
150: .getServletContext().getInitParameter(
151: "smtp-server-password");
152: }
153: } else if (_logger.isErrorEnabled())
154: _logger
155: .error("Missing SMTP servername servlet initialization parameter: smtp-server");
156: }
157:
158: /**
159: * Setup an email for the given Blog and User. Sets the from address to the blog owner's
160: * email address and the blog's name. Sets the to address to the user's email address
161: * and the blog owner's email address.
162: *
163: * @param blog {@link Blog}
164: * @param user {@link User}
165: * @param email E-mail message
166: * @throws EmailException If there is an error setting e-mail attributes
167: */
168: protected void setupEmail(Blog blog, User user, Email email)
169: throws EmailException {
170: email.setCharset("UTF-8");
171: if (_session != null)
172: email.setMailSession(_session);
173: else if (!BlojsomUtils.checkNullOrBlank(_mailServerUsername)
174: && !BlojsomUtils.checkNullOrBlank(_mailServerPassword)) {
175: email.setHostName(_mailServer);
176: email.setAuthentication(_mailServerUsername,
177: _mailServerPassword);
178: } else {
179: email.setHostName(_mailServer);
180: }
181: email.setFrom(blog.getBlogOwnerEmail(), blog.getBlogName());
182: String authorizedUserEmail = user.getUserEmail();
183: if (BlojsomUtils.checkNullOrBlank(authorizedUserEmail))
184: authorizedUserEmail = blog.getBlogOwnerEmail();
185: String authorizedUser = user.getUserName();
186: if (BlojsomUtils.checkNullOrBlank(authorizedUser))
187: authorizedUser = user.getUserLogin();
188: email.addTo(authorizedUserEmail, authorizedUser);
189: email.setSentDate(new Date());
190: }
191:
192: /**
193: * @see org.blojsom.plugin.admin.BaseAdminPlugin#process(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse,org.blojsom.blog.Blog,java.util.Map,org.blojsom.blog.Entry[])
194: */
195: public Entry[] process(HttpServletRequest httpServletRequest,
196: HttpServletResponse httpServletResponse, Blog blog,
197: Map context, Entry entries[]) throws PluginException {
198: String username = BlojsomUtils.getRequestValue(
199: REGISTRATION_USERNAME_PARAM, httpServletRequest);
200: String fullname = BlojsomUtils.getRequestValue(
201: REGISTRATION_FULLNAME_PARAM, httpServletRequest);
202: String userEmail = BlojsomUtils.getRequestValue(
203: REGISTRATION_EMAIL_PARAM, httpServletRequest);
204: String action = BlojsomUtils.getRequestValue(ACTION_PARAM,
205: httpServletRequest);
206: if (action != null
207: && REGISTRATION_ACTIVATION_ACTION.equals(action)) {
208: User user;
209: try {
210: user = _fetcher.loadUser(blog, username);
211: user.setUserStatus(APPROVED_STATUS);
212: _fetcher.saveUser(blog, user);
213: } catch (FetcherException e) {
214: if (_logger.isErrorEnabled())
215: _logger.error(e);
216: addOperationResultMessage(context,
217: formatRegistrationResource(
218: REGISTRATION_ACTIVATION_FAILED_KEY,
219: REGISTRATION_ACTIVATION_FAILED_KEY,
220: blog.getBlogLocale(),
221: new Object[] { username }));
222: httpServletRequest.setAttribute(PAGE_ACTION,
223: REGISTRATION_PAGE);
224: return entries;
225: }
226: addOperationResultMessage(context,
227: formatRegistrationResource(
228: REGISTRATION_ACTIVATION_SUCCESS_KEY,
229: REGISTRATION_ACTIVATION_SUCCESS_KEY, blog
230: .getBlogLocale(), new Object[] {
231: user.getUserName(),
232: user.getUserLogin() }));
233: httpServletRequest.setAttribute("page", REGISTRATION_PAGE);
234: return entries;
235: }
236: if (!BlojsomUtils.checkNullOrBlank(username)) {
237: if (fullname == null || ("").equals(fullname)) {
238: notifyOfMissingParameter("name", context, blog,
239: httpServletRequest);
240: return entries;
241: }
242: if (userEmail == null || ("").equals(userEmail)) {
243: notifyOfMissingParameter("email address", context,
244: blog, httpServletRequest);
245: return entries;
246: }
247: try {
248: Date now = new Date();
249: User user = new DatabaseUser();
250: user.setBlogId(blog.getId());
251: user.setUserLogin(username);
252: user.setUserName(fullname);
253: user.setUserEmail(userEmail);
254: user.setUserRegistered(now);
255: user.setUserStatus(REGISTERED_STATUS);
256: try {
257: addUserMetaData(
258: httpServletRequest,
259: blog,
260: context,
261: user,
262: blog
263: .getProperty(REGISTRATION_USER_REQUIRED_METADATA_KEYS),
264: true);
265: } catch (MissingParameterException e) {
266: return entries;
267: }
268: addUserMetaData(
269: httpServletRequest,
270: blog,
271: context,
272: user,
273: blog
274: .getProperty(REGISTRATION_USER_OPTIONAL_METADATA_KEYS),
275: false);
276: Random random = new Random(now.getTime()
277: + System.currentTimeMillis());
278: int password = random.nextInt(0x7fffffff);
279: String updatedPassword = Integer.toString(password);
280:
281: if (blog.getUseEncryptedPasswords().booleanValue()) {
282: user
283: .setUserPassword(BlojsomUtils.digestString(
284: updatedPassword, blog
285: .getDigestAlgorithm()));
286: } else {
287: user.setUserPassword(updatedPassword);
288: }
289: _fetcher.saveUser(blog, user);
290:
291: if (blog.getUseEncryptedPasswords().booleanValue()) {
292: // Set the user's password to the plain text version so we can email it to them; don't save it this way
293: user.setUserPassword(updatedPassword);
294: }
295:
296: Notification notification = constructEmail(blog, user,
297: httpServletRequest.getParameter("flavor"));
298: notification.send();
299:
300: if (_logger.isDebugEnabled()) {
301: _logger
302: .debug((new StringBuffer())
303: .append(
304: "Constructed registration e-mail message for username: ")
305: .append(username).toString());
306: }
307: addOperationResultMessage(context,
308: formatRegistrationResource(
309: CONSTRUCTED_REGISTRATION_EMAIL_KEY,
310: CONSTRUCTED_REGISTRATION_EMAIL_KEY,
311: blog.getBlogLocale(), new Object[] {
312: fullname, userEmail }));
313: httpServletRequest.setAttribute(PAGE_ACTION,
314: REGISTRATION_PAGE);
315: } catch (FetcherException e) {
316: if (_logger.isErrorEnabled()) {
317: _logger.error(e);
318: }
319: notifyOfFailedRegistration(username, context, blog,
320: httpServletRequest);
321: return entries;
322: } catch (EmailException e) {
323: if (_logger.isErrorEnabled()) {
324: _logger.error(e);
325: }
326: notifyOfFailedRegistration(username, context, blog,
327: httpServletRequest);
328: return entries;
329: } catch (MessagingException e) {
330: if (_logger.isErrorEnabled()) {
331: _logger.error(e);
332: }
333: notifyOfFailedRegistration(username, context, blog,
334: httpServletRequest);
335: return entries;
336: }
337: } else {
338: if (BlojsomUtils.checkNullOrBlank(action)) {
339: addOperationResultMessage(context, getAdminResource(
340: USERNAME_BLANK_KEY, USERNAME_BLANK_KEY, blog
341: .getBlogLocale()));
342: }
343: httpServletRequest.setAttribute(PAGE_ACTION,
344: REGISTRATION_PAGE);
345: }
346: return entries;
347: }
348:
349: /**
350: * Add metadata to the user. If the metadata is required and it's not present
351: * the user will be notified that required data is missing.
352: *
353: * @param httpServletRequest the request
354: * @param blog the Blog
355: * @param context Context
356: * @param user the user who's registering
357: * @param metaDataPropertyKeys the keys used to access the metadata values from the context
358: * @param isRequired If the metadata is required
359: * @throws MissingParameterException when a required parameter is missing
360: */
361: protected void addUserMetaData(
362: HttpServletRequest httpServletRequest, Blog blog,
363: Map context, User user, String metaDataPropertyKeys,
364: boolean isRequired) throws MissingParameterException {
365: if (metaDataPropertyKeys != null) {
366: String keys[] = metaDataPropertyKeys.split(",");
367: if (keys.length > 0) {
368: Map metaData = new HashMap();
369: for (int i = 0; i < keys.length; i++) {
370: String key = keys[i].trim();
371: try {
372: String formValue = BlojsomUtils
373: .getRequestValue(key,
374: httpServletRequest);
375: if (formValue != null && !"".equals(formValue)) {
376: metaData.put(key, formValue);
377: } else if (isRequired) {
378: notifyOfMissingParameter(key, context,
379: blog, httpServletRequest);
380: throw new MissingParameterException();
381: }
382: } catch (NullPointerException e) {
383: if (_logger.isErrorEnabled())
384: _logger.error((new StringBuffer()).append(
385: "Property ").append(key).append(
386: " was null").toString(), e);
387: }
388: }
389:
390: user.setMetaData(metaData);
391: }
392: }
393: }
394:
395: /**
396: * @see org.blojsom.plugin.admin.BaseAdminPlugin#cleanup()
397: */
398: public void cleanup() throws PluginException {
399: }
400:
401: /**
402: * @see org.blojsom.plugin.admin.BaseAdminPlugin#destroy()
403: */
404: public void destroy() throws PluginException {
405: }
406:
407: /**
408: * Construct an email notification to the user with their registration details.
409: *
410: * @param blog {@link Blog}
411: * @param user {@link User}
412: * @param flavor Flavor
413: * @return the Notification
414: * @throws EmailException If there is an error setting e-mail attributes
415: * @throws MessagingException If there is an error sending the e-mail
416: */
417: protected Notification constructEmail(Blog blog, User user,
418: String flavor) throws EmailException, MessagingException {
419: HtmlEmail email = new HtmlEmail();
420: setupEmail(blog, user, email);
421: String to = user.getUserName();
422: if (BlojsomUtils.checkNullOrBlank(to)) {
423: to = user.getUserLogin();
424: }
425: email.setSubject((new StringBuffer()).append(
426: "Registration details for ").append(to).toString());
427: String templatePath = (new StringBuffer()).append(
428: "/WEB-INF/themes/").append(flavor)
429: .append("/templates/").append(flavor).append(
430: "-registration-email.vm").toString();
431: java.net.URL emailTemplate;
432: try {
433: emailTemplate = _servletConfig.getServletContext()
434: .getResource(templatePath);
435: } catch (MalformedURLException e) {
436: throw new MessagingException("Cannot find email template",
437: e);
438: }
439: Notification notification = new RegistrationNotification(email,
440: emailTemplate, user, blog);
441: email.buildMimeMessage();
442: return notification;
443: }
444:
445: /**
446: * Retrieve a resource from the registration resource bundle and pass it through the {@link ResourceManager#format(String,Object[])} method
447: *
448: * @param resourceID ID of resource to retrieve
449: * @param fallbackText Text to use as fallback if resource ID is not found
450: * @param locale {@link Locale} to use when retrieving resource
451: * @param arguments Arguments for {@link ResourceManager#format(String,Object[])}
452: * @return Text from administration resource bundle given by <code>resourceID</code> formatted appropriately or <code>fallbackText</code> if the resource ID could not be formatted
453: */
454: protected String formatRegistrationResource(String resourceID,
455: String fallbackText, Locale locale, Object[] arguments) {
456: String resourceText = getRegistrationResource(resourceID,
457: fallbackText, locale);
458:
459: String formattedText = _resourceManager.format(resourceText,
460: arguments);
461: if (formattedText == null) {
462: formattedText = fallbackText;
463: }
464:
465: return formattedText;
466: }
467:
468: /**
469: * Retrieve a resource from the registration resource bundle
470: *
471: * @param resourceID ID of resource to retrieve
472: * @param fallbackText Text to use as fallback if resource ID is not found
473: * @param locale {@link Locale} to use when retrieving resource
474: * @return Text from administration resource bundle given by <code>resourceID</code> or <code>fallbackText</code> if the resource ID is not found
475: */
476: protected String getRegistrationResource(String resourceID,
477: String fallbackText, Locale locale) {
478: return _resourceManager.getString(resourceID,
479: REGISTRATION_MESSAGES_RESOURCE, fallbackText, locale);
480: }
481:
482: /**
483: * Notify the user that a required registration parameter is missing.
484: *
485: * @param parameterName the name of the missing parameter
486: * @param context Context
487: * @param blog the Blog
488: * @param httpServletRequest the request
489: */
490: protected void notifyOfMissingParameter(String parameterName,
491: Map context, Blog blog,
492: HttpServletRequest httpServletRequest) {
493: addOperationResultMessage(context, formatRegistrationResource(
494: REGISTRATION_MISSING_REQUIRED_VALUE_KEY,
495: REGISTRATION_MISSING_REQUIRED_VALUE_KEY, blog
496: .getBlogLocale(),
497: new Object[] { parameterName }));
498: httpServletRequest.setAttribute(PAGE_ACTION, REGISTRATION_PAGE);
499: }
500:
501: /**
502: * Notify the user that their registration has failed for some reason.
503: *
504: * @param username the username of the user for which registration failed
505: * @param context Context
506: * @param blog the Blog
507: * @param httpServletRequest the request
508: */
509: protected void notifyOfFailedRegistration(String username,
510: Map context, Blog blog,
511: HttpServletRequest httpServletRequest) {
512:
513: addOperationResultMessage(context, formatRegistrationResource(
514: FAILED_REGISTRATION_KEY, FAILED_REGISTRATION_KEY, blog
515: .getBlogLocale(), new Object[] { username }));
516: httpServletRequest.setAttribute(PAGE_ACTION, REGISTRATION_PAGE);
517:
518: }
519:
520: }
|