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.security;
031:
032: import org.apache.commons.codec.binary.Base64;
033: import org.blojsom.authorization.AuthorizationException;
034: import org.blojsom.authorization.AuthorizationProvider;
035: import org.blojsom.blog.Blog;
036: import org.blojsom.blog.Entry;
037: import org.blojsom.plugin.PluginException;
038: import org.blojsom.plugin.admin.BaseAdminPlugin;
039: import org.blojsom.util.BlojsomConstants;
040:
041: import javax.servlet.http.HttpServletRequest;
042: import javax.servlet.http.HttpServletResponse;
043: import javax.servlet.http.HttpSession;
044: import java.io.UnsupportedEncodingException;
045: import java.text.MessageFormat;
046: import java.util.Map;
047:
048: /**
049: * Basic Authentication plugin performs a BASIC authorization check so that users much authenticate
050: * before they are able to see any blog entries.
051: *
052: * @author David Czarnecki
053: * @version $Id: BasicAuthenticationPlugin.java,v 1.4 2007/01/17 02:35:13 czarneckid Exp $
054: * @since blojsom 3.0
055: */
056: public class BasicAuthenticationPlugin extends BaseAdminPlugin {
057:
058: private static final String AUTHORIZATION_HEADER = "Authorization";
059: private static final String WWW_AUTHENTICATE_HEADER = "WWW-Authenticate";
060: private static final String BASIC_REALM_HEADER = "Basic realm=\"{0}\"";
061: private static final String FAILED_AUTHORIZATION_PAGE = "/org/blojsom/plugin/security/templates/failed-authorization";
062:
063: private AuthorizationProvider _authorizationProvider;
064:
065: /**
066: * Construct a new instance of the Basic Authentication plugin
067: */
068: public BasicAuthenticationPlugin() {
069: }
070:
071: /**
072: * Set the {@link AuthorizationProvider}
073: *
074: * @param authorizationProvider {@link AuthorizationProvider}
075: */
076: public void setAuthorizationProvider(
077: AuthorizationProvider authorizationProvider) {
078: _authorizationProvider = authorizationProvider;
079: }
080:
081: /**
082: * Set the appropriate headers for BASIC authentication
083: *
084: * @param httpServletResponse Response
085: * @param blog {@link Blog}
086: */
087: protected void setAuthenticationRequired(
088: HttpServletResponse httpServletResponse, Blog blog) {
089: httpServletResponse
090: .setStatus(HttpServletResponse.SC_UNAUTHORIZED);
091: httpServletResponse.setHeader(WWW_AUTHENTICATE_HEADER,
092: MessageFormat.format(BASIC_REALM_HEADER,
093: new String[] { blog.getBlogName() }));
094: }
095:
096: /**
097: * Decode the BASIC authentication credentials and check the username/password against
098: * the authorized users for the blog.
099: *
100: * @param httpServletRequest Request
101: * @param blog {@link Blog}
102: * @return <code>true</code> if the BASIC authentication credentials are available and pass authentication,
103: * <code>false</code> otherwise
104: */
105: protected boolean decodeCredentialsAndAuthenticate(
106: HttpServletRequest httpServletRequest, Blog blog) {
107: String authorization = httpServletRequest
108: .getHeader(AUTHORIZATION_HEADER);
109: if (authorization != null) {
110: String encodedCredentials = authorization.substring(6)
111: .trim();
112:
113: try {
114: String usernameAndPassword = new String(Base64
115: .decodeBase64(encodedCredentials
116: .getBytes(BlojsomConstants.UTF8)));
117: int colonIndex = usernameAndPassword.indexOf(":");
118: if (colonIndex > 0) {
119: String username = usernameAndPassword.substring(0,
120: colonIndex);
121: String password = usernameAndPassword
122: .substring(colonIndex + 1);
123:
124: try {
125: _authorizationProvider.authorize(blog, null,
126: username, password);
127: HttpSession httpSession = httpServletRequest
128: .getSession();
129: httpSession
130: .setAttribute(
131: blog.getBlogAdminURL()
132: + "_"
133: + BLOJSOM_ADMIN_PLUGIN_AUTHENTICATED_KEY,
134: Boolean.TRUE);
135: httpSession.setAttribute(blog.getBlogAdminURL()
136: + "_"
137: + BLOJSOM_ADMIN_PLUGIN_USERNAME_KEY,
138: username);
139: httpSession
140: .setAttribute(
141: BLOJSOM_ADMIN_PLUGIN_USERNAME,
142: username);
143: httpSession.setAttribute(
144: BLOJSOM_USER_AUTHENTICATED,
145: Boolean.TRUE);
146: return true;
147: } catch (AuthorizationException e) {
148: if (_logger.isErrorEnabled()) {
149: _logger.error(e);
150: }
151:
152: return false;
153: }
154: }
155: } catch (UnsupportedEncodingException e) {
156: if (_logger.isErrorEnabled()) {
157: _logger.error(e);
158: }
159:
160: return false;
161: }
162: } else {
163: // Check to see if the user is already authenticated through means other than Basic Auth
164: Boolean isAuthenticated = (Boolean) httpServletRequest
165: .getSession().getAttribute(
166: BLOJSOM_USER_AUTHENTICATED);
167: if (isAuthenticated != null) {
168: return isAuthenticated.booleanValue();
169: }
170: }
171:
172: return false;
173: }
174:
175: /**
176: * Process the blog entries
177: *
178: * @param httpServletRequest Request
179: * @param httpServletResponse Response
180: * @param blog {@link Blog} instance
181: * @param context Context
182: * @param entries Blog entries retrieved for the particular request
183: * @return Modified set of blog entries
184: * @throws PluginException If there is an error processing the blog entries
185: */
186: public Entry[] process(HttpServletRequest httpServletRequest,
187: HttpServletResponse httpServletResponse, Blog blog,
188: Map context, Entry[] entries) throws PluginException {
189: if (!decodeCredentialsAndAuthenticate(httpServletRequest, blog)) {
190: setAuthenticationRequired(httpServletResponse, blog);
191:
192: httpServletRequest.setAttribute(
193: BlojsomConstants.PAGE_PARAM,
194: FAILED_AUTHORIZATION_PAGE);
195:
196: return new Entry[0];
197: }
198:
199: return entries;
200: }
201:
202: /**
203: * Perform any cleanup for the plugin. Called after {@link #process}.
204: *
205: * @throws org.blojsom.plugin.PluginException
206: * If there is an error performing cleanup for this plugin
207: */
208: public void cleanup() throws PluginException {
209: }
210:
211: /**
212: * Called when BlojsomServlet is taken out of service
213: *
214: * @throws org.blojsom.plugin.PluginException
215: * If there is an error in finalizing this plugin
216: */
217: public void destroy() throws PluginException {
218: }
219: }
|