001: /*
002: JSPWiki - a JSP-based WikiWiki clone.
003:
004: Copyright (C) 2001-2007 Janne Jalkanen (Janne.Jalkanen@iki.fi)
005:
006: This program is free software; you can redistribute it and/or modify
007: it under the terms of the GNU Lesser General Public License as published by
008: the Free Software Foundation; either version 2.1 of the License, or
009: (at your option) any later version.
010:
011: This program is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: GNU Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser General Public License
017: along with this program; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020: package com.ecyrd.jspwiki.auth.login;
021:
022: import java.io.IOException;
023:
024: import javax.security.auth.callback.Callback;
025: import javax.security.auth.callback.UnsupportedCallbackException;
026: import javax.security.auth.login.FailedLoginException;
027: import javax.security.auth.login.LoginException;
028: import javax.servlet.http.Cookie;
029: import javax.servlet.http.HttpServletRequest;
030: import javax.servlet.http.HttpServletResponse;
031: import javax.servlet.http.HttpSession;
032:
033: import org.apache.log4j.Logger;
034:
035: import com.ecyrd.jspwiki.TextUtil;
036: import com.ecyrd.jspwiki.auth.WikiPrincipal;
037: import com.ecyrd.jspwiki.auth.authorize.Role;
038: import com.ecyrd.jspwiki.util.HttpUtil;
039:
040: /**
041: * <p>
042: * Logs in a user based on assertion of a name supplied in a cookie. If the
043: * cookie is not found, authentication fails.
044: * </p>
045: * This module must be used with a CallbackHandler (such as
046: * {@link WebContainerCallbackHandler}) that supports the following Callback
047: * types:
048: * </p>
049: * <ol>
050: * <li>{@link HttpRequestCallback}- supplies the cookie, which should contain
051: * a user name.</li>
052: * </ol>
053: * <p>
054: * After authentication, a generic WikiPrincipal based on the username will be
055: * created and associated with the Subject. Principals
056: * {@link com.ecyrd.jspwiki.auth.authorize.Role#ALL} and
057: * {@link com.ecyrd.jspwiki.auth.authorize.Role#ASSERTED} will be added.
058: * </p>
059: * @see javax.security.auth.spi.LoginModule#commit()
060: * @see CookieAuthenticationLoginModule
061: * @author Andrew Jaquith
062: * @since 2.3
063: */
064: public class CookieAssertionLoginModule extends AbstractLoginModule {
065:
066: /** The name of the cookie that gets stored to the user browser. */
067: public static final String PREFS_COOKIE_NAME = "JSPWikiAssertedName";
068:
069: /** Believed to be unused.
070: * @deprecated */
071: public static final String PROMPT = "User name";
072:
073: protected static final Logger log = Logger
074: .getLogger(CookieAssertionLoginModule.class);
075:
076: /**
077: * Logs in the user by calling back to the registered CallbackHandler with
078: * an HttpRequestCallback. The CallbackHandler must supply the current
079: * servlet HTTP request as its response.
080: * @return the result of the login; if the subject Principal set already
081: * possesses {@link Role#AUTHENTICATED}, always returns <code>false</code>
082: * to indicate that this module should be ignored. Otherwise, if a cookie is
083: * found, this method returns <code>true</code>. If not found, this
084: * method throws a <code>FailedLoginException</code>.
085: * @see javax.security.auth.spi.LoginModule#login()
086: */
087: public boolean login() throws LoginException {
088: // Ignore this module if already authenticated
089: if (m_subject.getPrincipals().contains(Role.AUTHENTICATED)) {
090: // If login ignored, remove asserted role
091: m_principalsToRemove.add(Role.ASSERTED);
092: return false;
093: }
094:
095: // Otherwise, let's go and look for the cookie!
096: HttpRequestCallback hcb = new HttpRequestCallback();
097: Callback[] callbacks = new Callback[] { hcb };
098: try {
099: m_handler.handle(callbacks);
100: HttpServletRequest request = hcb.getRequest();
101: HttpSession session = (request == null) ? null : request
102: .getSession(false);
103: String sid = (session == null) ? NULL : session.getId();
104: String name = getUserCookie(request);
105: if (name == null) {
106: if (log.isDebugEnabled()) {
107: log.debug("No cookie " + PREFS_COOKIE_NAME
108: + " present in session ID=: " + sid);
109: }
110: throw new FailedLoginException(
111: "The user cookie was not found.");
112: }
113:
114: if (log.isDebugEnabled()) {
115: log.debug("Logged in session ID=" + sid);
116: log.debug("Added Principals " + name
117: + ",Role.ASSERTED,Role.ALL");
118: }
119: // If login succeeds, commit these principals/roles
120: m_principals.add(new WikiPrincipal(name,
121: WikiPrincipal.FULL_NAME));
122: m_principals.add(Role.ASSERTED);
123: m_principals.add(Role.ALL);
124:
125: // If login succeeds, overwrite these principals/roles
126: m_principalsToOverwrite.add(WikiPrincipal.GUEST);
127: m_principalsToOverwrite.add(Role.ANONYMOUS);
128:
129: // If login fails, remove these roles
130: m_principalsToRemove.add(Role.ASSERTED);
131: return true;
132: } catch (IOException e) {
133: log.error("IOException: " + e.getMessage());
134: return false;
135: } catch (UnsupportedCallbackException e) {
136: String message = "Unable to handle callback, disallowing login.";
137: log.error(message, e);
138: throw new LoginException(message);
139: }
140:
141: }
142:
143: /**
144: * Returns the username cookie value.
145: *
146: * @param request The Servlet request, as usual.
147: * @return the username, as retrieved from the cookie
148: */
149: public static String getUserCookie(HttpServletRequest request) {
150: String cookie = HttpUtil.retrieveCookieValue(request,
151: PREFS_COOKIE_NAME);
152:
153: return TextUtil.urlDecodeUTF8(cookie);
154: }
155:
156: /**
157: * Sets the username cookie. The cookie value is URLEncoded in UTF-8.
158: *
159: * @param response The Servlet response
160: * @param name The name to write into the cookie.
161: */
162: public static void setUserCookie(HttpServletResponse response,
163: String name) {
164: name = TextUtil.urlEncodeUTF8(name);
165: Cookie userId = new Cookie(PREFS_COOKIE_NAME, name);
166: userId.setMaxAge(1001 * 24 * 60 * 60); // 1001 days is default.
167: response.addCookie(userId);
168: }
169:
170: /**
171: * Removes the user cookie from the response. This makes the user appear
172: * again as an anonymous coward.
173: *
174: * @param response The servlet response.
175: */
176: public static void clearUserCookie(HttpServletResponse response) {
177: Cookie userId = new Cookie(PREFS_COOKIE_NAME, "");
178: userId.setMaxAge(0);
179: response.addCookie(userId);
180: }
181: }
|