001: /*
002: * Copyright (c) JForum Team
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms,
006: * with or without modification, are permitted provided
007: * that the following conditions are met:
008: *
009: * 1) Redistributions of source code must retain the above
010: * copyright notice, this list of conditions and the
011: * following disclaimer.
012: * 2) Redistributions in binary form must reproduce the
013: * above copyright notice, this list of conditions and
014: * the following disclaimer in the documentation and/or
015: * other materials provided with the distribution.
016: * 3) Neither the name of "Rafael Steil" nor
017: * the names of its contributors may be used to endorse
018: * or promote products derived from this software without
019: * specific prior written permission.
020: *
021: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
022: * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
023: * EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
024: * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
025: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR
026: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
027: * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
028: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
029: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES
030: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
031: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
032: * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
033: * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
034: * IN CONTRACT, STRICT LIABILITY, OR TORT
035: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
036: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
037: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
038: *
039: * Created on Mar 17, 2005 5:38:11 PM
040: *
041: * The JForum Project
042: * http://www.jforum.net
043: */
044: package net.jforum;
045:
046: import java.util.Date;
047:
048: import javax.servlet.http.Cookie;
049:
050: import net.jforum.context.ForumContext;
051: import net.jforum.context.RequestContext;
052: import net.jforum.context.SessionContext;
053: import net.jforum.dao.DataAccessDriver;
054: import net.jforum.dao.UserDAO;
055: import net.jforum.dao.UserSessionDAO;
056: import net.jforum.entities.User;
057: import net.jforum.entities.UserSession;
058: import net.jforum.exceptions.DatabaseException;
059: import net.jforum.exceptions.ForumException;
060: import net.jforum.repository.SecurityRepository;
061: import net.jforum.security.SecurityConstants;
062: import net.jforum.sso.SSO;
063: import net.jforum.sso.SSOUtils;
064: import net.jforum.util.I18n;
065: import net.jforum.util.MD5;
066: import net.jforum.util.preferences.ConfigKeys;
067: import net.jforum.util.preferences.SystemGlobals;
068: import freemarker.template.SimpleHash;
069:
070: /**
071: * Common methods used by the controller.
072: *
073: * @author Rafael Steil
074: * @version $Id: ControllerUtils.java,v 1.38 2007/08/01 22:30:06 rafaelsteil Exp $
075: */
076: public class ControllerUtils {
077: /**
078: * Setup common variables used by almost all templates.
079: *
080: * @param context SimpleHash The context to use
081: * @param jforumContext JForumContext
082: */
083: public void prepareTemplateContext(SimpleHash context,
084: ForumContext jforumContext) {
085: RequestContext request = JForumExecutionContext.getRequest();
086:
087: context.put("karmaEnabled", SecurityRepository
088: .canAccess(SecurityConstants.PERM_KARMA_ENABLED));
089: context.put("dateTimeFormat", SystemGlobals
090: .getValue(ConfigKeys.DATE_TIME_FORMAT));
091: context.put("autoLoginEnabled", SystemGlobals
092: .getBoolValue(ConfigKeys.AUTO_LOGIN_ENABLED));
093: context.put("sso", ConfigKeys.TYPE_SSO.equals(SystemGlobals
094: .getValue(ConfigKeys.AUTHENTICATION_TYPE)));
095: context.put("contextPath", request.getContextPath());
096: context.put("serverName", request.getServerName());
097: context.put("templateName", SystemGlobals
098: .getValue(ConfigKeys.TEMPLATE_DIR));
099: context.put("extension", SystemGlobals
100: .getValue(ConfigKeys.SERVLET_EXTENSION));
101: context.put("serverPort", Integer.toString(request
102: .getServerPort()));
103: context.put("I18n", I18n.getInstance());
104: context.put("version", SystemGlobals
105: .getValue(ConfigKeys.VERSION));
106: context.put("forumTitle", SystemGlobals
107: .getValue(ConfigKeys.FORUM_PAGE_TITLE));
108: context.put("pageTitle", SystemGlobals
109: .getValue(ConfigKeys.FORUM_PAGE_TITLE));
110: context.put("metaKeywords", SystemGlobals
111: .getValue(ConfigKeys.FORUM_PAGE_METATAG_KEYWORDS));
112: context.put("metaDescription", SystemGlobals
113: .getValue(ConfigKeys.FORUM_PAGE_METATAG_DESCRIPTION));
114: context.put("forumLink", SystemGlobals
115: .getValue(ConfigKeys.FORUM_LINK));
116: context.put("homepageLink", SystemGlobals
117: .getValue(ConfigKeys.HOMEPAGE_LINK));
118: context.put("encoding", SystemGlobals
119: .getValue(ConfigKeys.ENCODING));
120: context.put("bookmarksEnabled", SecurityRepository
121: .canAccess(SecurityConstants.PERM_BOOKMARKS_ENABLED));
122: context.put("canAccessModerationLog", SecurityRepository
123: .canAccess(SecurityConstants.PERM_MODERATION_LOG));
124: context.put("JForumContext", jforumContext);
125: context.put("timestamp", new Long(System.currentTimeMillis()));
126: }
127:
128: /**
129: * Checks user credentials / automatic login.
130: *
131: * @param userSession The UserSession instance associated to the user's session
132: * @return <code>true</code> if auto login was enabled and the user was sucessfuly
133: * logged in.
134: * @throws DatabaseException
135: */
136: protected boolean checkAutoLogin(UserSession userSession) {
137: String cookieName = SystemGlobals
138: .getValue(ConfigKeys.COOKIE_NAME_DATA);
139:
140: Cookie cookie = this .getCookieTemplate(cookieName);
141: Cookie hashCookie = this .getCookieTemplate(SystemGlobals
142: .getValue(ConfigKeys.COOKIE_USER_HASH));
143: Cookie autoLoginCookie = this .getCookieTemplate(SystemGlobals
144: .getValue(ConfigKeys.COOKIE_AUTO_LOGIN));
145:
146: if (hashCookie != null
147: && cookie != null
148: && !cookie
149: .getValue()
150: .equals(
151: SystemGlobals
152: .getValue(ConfigKeys.ANONYMOUS_USER_ID))
153: && autoLoginCookie != null
154: && "1".equals(autoLoginCookie.getValue())) {
155: String uid = cookie.getValue();
156: String uidHash = hashCookie.getValue();
157:
158: // Load the user-specific security hash from the database
159: try {
160: UserDAO userDao = DataAccessDriver.getInstance()
161: .newUserDAO();
162: String userHash = userDao.getUserAuthHash(Integer
163: .parseInt(uid));
164:
165: if (userHash == null || userHash.trim().length() == 0) {
166: return false;
167: }
168:
169: String securityHash = MD5.crypt(userHash);
170:
171: if (securityHash.equals(uidHash)) {
172: int userId = Integer.parseInt(uid);
173: userSession.setUserId(userId);
174:
175: User user = userDao.selectById(userId);
176:
177: if (user == null || user.getId() != userId
178: || user.isDeleted()) {
179: userSession.makeAnonymous();
180: return false;
181: }
182:
183: this .configureUserSession(userSession, user);
184:
185: return true;
186: }
187: } catch (Exception e) {
188: throw new DatabaseException(e);
189: }
190:
191: userSession.makeAnonymous();
192: }
193:
194: return false;
195: }
196:
197: /**
198: * Setup optios and values for the user's session if authentication was ok.
199: *
200: * @param userSession The UserSession instance of the user
201: * @param user The User instance of the authenticated user
202: */
203: protected void configureUserSession(UserSession userSession,
204: User user) {
205: userSession.dataToUser(user);
206:
207: // As an user may come back to the forum before its
208: // last visit's session expires, we should check for
209: // existent user information and then, if found, store
210: // it to the database before getting his information back.
211: String sessionId = SessionFacade.isUserInSession(user.getId());
212:
213: UserSession tmpUs;
214: if (sessionId != null) {
215: SessionFacade.storeSessionData(sessionId,
216: JForumExecutionContext.getConnection());
217: tmpUs = SessionFacade.getUserSession(sessionId);
218: SessionFacade.remove(sessionId);
219: } else {
220: UserSessionDAO sm = DataAccessDriver.getInstance()
221: .newUserSessionDAO();
222: tmpUs = sm.selectById(userSession, JForumExecutionContext
223: .getConnection());
224: }
225:
226: if (tmpUs == null) {
227: userSession.setLastVisit(new Date(System
228: .currentTimeMillis()));
229: } else {
230: // Update last visit and session start time
231: userSession.setLastVisit(new Date(tmpUs.getStartTime()
232: .getTime()
233: + tmpUs.getSessionTime()));
234: }
235:
236: // If the execution point gets here, then the user
237: // has chosen "autoLogin"
238: userSession.setAutoLogin(true);
239: SessionFacade.makeLogged();
240:
241: I18n.load(user.getLang());
242: }
243:
244: /**
245: * Checks for user authentication using some SSO implementation
246: * @param userSession UserSession
247: */
248: protected void checkSSO(UserSession userSession) {
249: try {
250: SSO sso = (SSO) Class.forName(
251: SystemGlobals
252: .getValue(ConfigKeys.SSO_IMPLEMENTATION))
253: .newInstance();
254: String username = sso
255: .authenticateUser(JForumExecutionContext
256: .getRequest());
257:
258: if (username == null || username.trim().equals("")) {
259: userSession.makeAnonymous();
260: } else {
261: SSOUtils utils = new SSOUtils();
262:
263: if (!utils.userExists(username)) {
264: SessionContext session = JForumExecutionContext
265: .getRequest().getSessionContext();
266:
267: String email = (String) session
268: .getAttribute(SystemGlobals
269: .getValue(ConfigKeys.SSO_EMAIL_ATTRIBUTE));
270: String password = (String) session
271: .getAttribute(SystemGlobals
272: .getValue(ConfigKeys.SSO_PASSWORD_ATTRIBUTE));
273:
274: if (email == null) {
275: email = SystemGlobals
276: .getValue(ConfigKeys.SSO_DEFAULT_EMAIL);
277: }
278:
279: if (password == null) {
280: password = SystemGlobals
281: .getValue(ConfigKeys.SSO_DEFAULT_PASSWORD);
282: }
283:
284: utils.register(password, email);
285: }
286:
287: this .configureUserSession(userSession, utils.getUser());
288: }
289: } catch (Exception e) {
290: e.printStackTrace();
291: throw new ForumException(
292: "Error while executing SSO actions: " + e);
293: }
294: }
295:
296: /**
297: * Do a refresh in the user's session. This method will update the last visit time for the
298: * current user, as well checking for authentication if the session is new or the SSO user has
299: * changed
300: */
301: public void refreshSession() {
302: UserSession userSession = SessionFacade.getUserSession();
303: RequestContext request = JForumExecutionContext.getRequest();
304:
305: if (userSession == null) {
306: userSession = new UserSession();
307: userSession.registerBasicInfo();
308: userSession.setSessionId(request.getSessionContext()
309: .getId());
310: userSession.setIp(request.getRemoteAddr());
311: SessionFacade.makeUnlogged();
312:
313: if (!JForumExecutionContext.getForumContext().isBot()) {
314: // Non-SSO authentications can use auto login
315: if (!ConfigKeys.TYPE_SSO.equals(SystemGlobals
316: .getValue(ConfigKeys.AUTHENTICATION_TYPE))) {
317: if (SystemGlobals
318: .getBoolValue(ConfigKeys.AUTO_LOGIN_ENABLED)) {
319: this .checkAutoLogin(userSession);
320: } else {
321: userSession.makeAnonymous();
322: }
323: } else {
324: this .checkSSO(userSession);
325: }
326: }
327:
328: SessionFacade.add(userSession);
329: } else if (ConfigKeys.TYPE_SSO.equals(SystemGlobals
330: .getValue(ConfigKeys.AUTHENTICATION_TYPE))) {
331: SSO sso;
332:
333: try {
334: sso = (SSO) Class
335: .forName(
336: SystemGlobals
337: .getValue(ConfigKeys.SSO_IMPLEMENTATION))
338: .newInstance();
339: } catch (Exception e) {
340: throw new ForumException(e);
341: }
342:
343: // If SSO, then check if the session is valid
344: if (!sso.isSessionValid(userSession, request)) {
345: SessionFacade.remove(userSession.getSessionId());
346: refreshSession();
347: }
348: } else {
349: SessionFacade.getUserSession().updateSessionTime();
350: }
351: }
352:
353: /**
354: * Gets a cookie by its name.
355: *
356: * @param name The cookie name to retrieve
357: * @return The <code>Cookie</code> object if found, or <code>null</code> oterwhise
358: */
359: public static Cookie getCookie(String name) {
360: Cookie[] cookies = JForumExecutionContext.getRequest()
361: .getCookies();
362:
363: if (cookies != null) {
364: for (int i = 0; i < cookies.length; i++) {
365: Cookie c = cookies[i];
366:
367: if (c.getName().equals(name)) {
368: return c;
369: }
370: }
371: }
372:
373: return null;
374: }
375:
376: /**
377: * Template method to get a cookie.
378: * Useful to situations when a subclass
379: * wants to have a different way to
380: * retrieve a cookie.
381: * @param name The cookie name to retrieve
382: * @return The Cookie object if found, or null otherwise
383: * @see #getCookie(String)
384: */
385: protected Cookie getCookieTemplate(String name) {
386: return ControllerUtils.getCookie(name);
387: }
388:
389: /**
390: * Add or update a cookie. This method adds a cookie, serializing its value using XML.
391: *
392: * @param name The cookie name.
393: * @param value The cookie value
394: */
395: public static void addCookie(String name, String value) {
396: int maxAge = 3600 * 24 * 365;
397:
398: if (value == null) {
399: maxAge = 0;
400: value = "";
401: }
402:
403: Cookie cookie = new Cookie(name, value);
404: cookie.setMaxAge(maxAge);
405: cookie.setPath("/");
406:
407: JForumExecutionContext.getResponse().addCookie(cookie);
408: }
409:
410: /**
411: * Template method to add a cookie.
412: * Useful to suatins when a subclass wants to add
413: * a cookie in a fashion different than the normal
414: * behaviour
415: * @param name The cookie name
416: * @param value The cookie value
417: * @see #addCookie(String, String)
418: */
419: protected void addCookieTemplate(String name, String value) {
420: ControllerUtils.addCookie(name, value);
421: }
422: }
|