001: /*
002: * argun 1.0
003: * Web 2.0 delivery framework
004: * Copyright (C) 2007 Hammurapi Group
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (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 GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * URL: http://www.hammurapi.biz
021: * e-Mail: support@hammurapi.biz
022: */
023:
024: package biz.hammurapi.web.security;
025:
026: import java.io.IOException;
027: import java.io.PrintWriter;
028: import java.sql.SQLException;
029: import java.util.ArrayList;
030: import java.util.Collection;
031: import java.util.HashMap;
032: import java.util.Iterator;
033: import java.util.Map;
034:
035: import javax.servlet.Filter;
036: import javax.servlet.FilterChain;
037: import javax.servlet.FilterConfig;
038: import javax.servlet.ServletException;
039: import javax.servlet.ServletRequest;
040: import javax.servlet.ServletResponse;
041: import javax.servlet.http.HttpServletRequest;
042: import javax.servlet.http.HttpServletResponse;
043: import javax.servlet.http.HttpSession;
044:
045: import org.apache.commons.codec.binary.Base64;
046:
047: import biz.hammurapi.authorization.AuthorizationProvider;
048: import biz.hammurapi.config.Context;
049: import biz.hammurapi.web.HammurapiWebException;
050: import biz.hammurapi.web.menu.MenuFilter;
051: import biz.hammurapi.web.security.sql.ApplicationPermission;
052: import biz.hammurapi.web.security.sql.SecurityEngine;
053:
054: /**
055: * This class handles authentication - it makes sure that each request is authenticated
056: * @author Pavel Vlasov
057: * @version $Revision: 1.1 $
058: */
059: public class AuthFilter implements Filter {
060:
061: public static final String LOGIN_TARGET = "login-target";
062: public static final String LOGIN_URL_ATTRIBUTE = AuthFilter.class
063: .getName()
064: + ":login-url";
065: public static final String MAX_LOGIN_ATTEMPTS = "max-login-attempts";
066: public static final String AUTHORIZATION_PROVIDER = AuthorizationProvider.class
067: .getName();
068: public static final String USER = User.class.getName();
069: public static final String REMAINING_LOGIN_ATTEMPTS = "remaining-login-attempts";
070: private static final String DEBUG = "debug";
071: private String loginUrl;
072: private String realm;
073: private int maxAttempts = 6;
074: private String unauthenticatedPrincipal;
075: private boolean debug;
076: private Integer maxAttemptsInteger = new Integer(maxAttempts);
077: private FilterConfig filterConfig;
078:
079: /**
080: * Init method for this filter
081: *
082: */
083: public void init(final FilterConfig filterConfig)
084: throws ServletException {
085: filterConfig.getServletContext().log(
086: "Initializing authentication filter");
087: loginUrl = filterConfig.getInitParameter("login-url");
088: if (loginUrl != null) {
089: filterConfig.getServletContext()
090: .log("loginUrl=" + loginUrl);
091: }
092:
093: realm = filterConfig.getInitParameter("realm");
094: if (realm != null) {
095: filterConfig.getServletContext().log("realm=" + realm);
096: }
097:
098: String maStr = filterConfig.getInitParameter("max-attempts");
099: if (maStr != null) {
100: maxAttempts = Integer.parseInt(maStr);
101: maxAttemptsInteger = new Integer(maxAttempts);
102: }
103:
104: unauthenticatedPrincipal = filterConfig
105: .getInitParameter("unauthenticated-principal");
106: if (unauthenticatedPrincipal != null) {
107: filterConfig.getServletContext().log(
108: "unauthenticated principal="
109: + unauthenticatedPrincipal);
110: }
111:
112: debug = "yes".equals(filterConfig.getInitParameter(DEBUG));
113:
114: filterConfig.getServletContext().log(
115: "Max login attempts=" + maxAttempts);
116: filterConfig.getServletContext().setAttribute(
117: "filter/" + filterConfig.getFilterName(), this );
118: this .filterConfig = filterConfig;
119: }
120:
121: /**
122: * @return Name of unauthenticated principal (guest user) for this application.
123: */
124: public String getUnauthenticatedPrincipal() {
125: return unauthenticatedPrincipal;
126: }
127:
128: /**
129: * Checks user in session. If user is present then authorization provider is injected into request.
130: * If there is no credentials then Guest credentials are used if such user exists and is not blocked.
131: * If Guest user doesn't exists then control is passed to login url. If login URL is null then basic
132: * authentication is used.
133: * @param request The servlet request we are processing
134: * @param result The servlet response we are creating
135: * @param chain The filter chain we are processing
136: *
137: * @exception IOException if an input/output error occurs
138: * @exception ServletException if a servlet error occurs
139: */
140: public void doFilter(ServletRequest request,
141: ServletResponse response, FilterChain chain)
142: throws IOException, ServletException {
143: if (request instanceof HttpServletRequest
144: && response instanceof HttpServletResponse) {
145: HttpServletRequest httpRequest = (HttpServletRequest) request;
146: HttpServletResponse httpResponse = (HttpServletResponse) response;
147: HttpSession session = httpRequest.getSession();
148: session
149: .setAttribute(MAX_LOGIN_ATTEMPTS,
150: maxAttemptsInteger);
151: if (loginUrl != null) {
152: String ctxPath = httpRequest.getContextPath();
153: if (ctxPath.endsWith("/")) {
154: ctxPath = ctxPath
155: .substring(0, ctxPath.length() - 1);
156: }
157: request.setAttribute(LOGIN_URL_ATTRIBUTE, ctxPath
158: + loginUrl);
159: }
160:
161: try {
162: // Get authorization provider.
163: if (session.getAttribute(AUTHORIZATION_PROVIDER) != null) {
164: if (debug
165: || "yes".equals(request
166: .getParameter("reloadUser"))) {
167: reload(httpRequest);
168: }
169: chain.doFilter(request, response);
170: } else {
171: // Get guest user
172: if (authorize(httpRequest, null, null)) {
173: chain.doFilter(request, response);
174: } else {
175: if (loginUrl == null) {
176: // If login page is null then do basic authentication
177: String auth = httpRequest
178: .getHeader("Authorization");
179: if (auth == null) {
180: httpResponse
181: .setHeader("WWW-Authenticate",
182: "BASIC realm=\""
183: + realm + "\"");
184: httpResponse
185: .sendError(HttpServletResponse.SC_UNAUTHORIZED);
186: } else if (auth.toUpperCase().startsWith(
187: "BASIC ")) {
188: String decoded = new String(Base64
189: .decodeBase64(auth.substring(6)
190: .getBytes()));
191: int idx = decoded.indexOf(":");
192: if (idx != -1
193: && authorize(
194: httpRequest,
195: decoded.substring(0,
196: idx),
197: decoded
198: .substring(idx + 1))) {
199: session
200: .removeAttribute(REMAINING_LOGIN_ATTEMPTS);
201: chain.doFilter(request, response);
202: } else {
203: Integer attempts = (Integer) session
204: .getAttribute(REMAINING_LOGIN_ATTEMPTS);
205: if (attempts == null) {
206: session
207: .setAttribute(
208: REMAINING_LOGIN_ATTEMPTS,
209: maxAttemptsInteger);
210: } else {
211: if (attempts.intValue() <= 0) {
212: httpResponse
213: .sendError(
214: HttpServletResponse.SC_UNAUTHORIZED,
215: "Too many failed login attempts");
216: return;
217: }
218: session
219: .setAttribute(
220: REMAINING_LOGIN_ATTEMPTS,
221: new Integer(
222: attempts
223: .intValue() - 1));
224: }
225: httpResponse.setHeader(
226: "WWW-Authenticate",
227: "BASIC realm=\"" + realm
228: + "\"");
229: httpResponse
230: .sendError(HttpServletResponse.SC_UNAUTHORIZED);
231: }
232: } else {
233: httpResponse
234: .sendError(
235: HttpServletResponse.SC_UNAUTHORIZED,
236: "");
237: }
238: } else {
239: // System.out.println(httpRequest.getRequestURI());
240: if (loginUrl.equals(httpRequest
241: .getRequestURI().substring(
242: httpRequest
243: .getContextPath()
244: .length()))) {
245: chain.doFilter(request, response);
246: } else {
247: Integer attempts = (Integer) session
248: .getAttribute(REMAINING_LOGIN_ATTEMPTS);
249: if (attempts == null) {
250: session.setAttribute(
251: REMAINING_LOGIN_ATTEMPTS,
252: maxAttemptsInteger);
253: } else {
254: if (attempts.intValue() <= 0) {
255: httpResponse
256: .sendError(
257: HttpServletResponse.SC_UNAUTHORIZED,
258: "Too many failed login attempts");
259: return;
260: }
261: session.setAttribute(
262: REMAINING_LOGIN_ATTEMPTS,
263: new Integer(attempts
264: .intValue() - 1));
265: }
266: // If guest user is null - redirect to login page
267: String queryString = httpRequest
268: .getQueryString();
269: if (queryString == null) {
270: session.setAttribute(LOGIN_TARGET,
271: httpRequest.getRequestURL()
272: .toString());
273: } else {
274: session
275: .setAttribute(
276: LOGIN_TARGET,
277: httpRequest
278: .getRequestURL()
279: .append("?")
280: .append(
281: queryString)
282: .toString());
283: }
284: httpResponse.sendRedirect(httpRequest
285: .getContextPath()
286: + loginUrl);
287: PrintWriter out = response.getWriter();
288: out.write("<HTML><HEAD></HEAD><BODY>");
289: out.write("Redirecting to <a href=\""
290: + httpRequest.getContextPath()
291: + loginUrl
292: + "\">login page</a>");
293: out.write("</BODY></HTML>");
294: }
295: }
296: }
297: }
298: } catch (HammurapiWebException e) {
299: throw new ServletException(
300: "Authorization failed: " + e, e);
301: }
302: } else {
303: chain.doFilter(request, response);
304: }
305: }
306:
307: public void destroy() {
308: // Nothing to do here.
309: }
310:
311: private Map classPermissions = new HashMap();
312:
313: /**
314: * Constructs authorization provider for user. Injects user into session.
315: * @param request
316: * @param loginName
317: * @param password
318: * @return
319: * @throws SQLException
320: */
321: private boolean authorize(HttpServletRequest request,
322: String loginName, String password)
323: throws HammurapiWebException {
324: try {
325: SecurityEngine engine = (SecurityEngine) ((Context) request
326: .getAttribute("global")).get("db/SecurityEngine");
327: if (loginName == null
328: && getUnauthenticatedPrincipal() == null) {
329: return false;
330: }
331:
332: User user = (User) engine.getApplicationUser(
333: loginName == null ? getUnauthenticatedPrincipal()
334: : loginName, User.class);
335:
336: if (user == null || user.getIsBlocked()) {
337: return false;
338: }
339:
340: if (loginName != null
341: && (password == null || !ApplicationUserActions
342: .hashPassword(password).equals(
343: user.getUserPassword()))) {
344: return false;
345: }
346:
347: Collection permissions = engine
348: .getApplicationPermission(new ArrayList());
349: Iterator it = permissions.iterator();
350: while (it.hasNext()) {
351: ApplicationPermission ap = (ApplicationPermission) it
352: .next();
353: if (!Boolean.TRUE.equals(user.hasPermission(ap
354: .getClassName(), ap.getActionName()))) {
355: it.remove();
356: }
357: }
358:
359: HttpSession session = request.getSession();
360: session.setAttribute(USER, user);
361: session.setAttribute(AUTHORIZATION_PROVIDER,
362: new UserAuthorizationProvider(user, permissions,
363: classPermissions));
364: return getMenuFilter().buildMenu(request);
365: } catch (SQLException e) {
366: throw new HammurapiWebException("Authorization failed: "
367: + e, e);
368: }
369: }
370:
371: private MenuFilter getMenuFilter() {
372: return (MenuFilter) filterConfig.getServletContext()
373: .getAttribute("filter/MenuFilter");
374: }
375:
376: /**
377: * Reloads user and menu at each request. Useful during develpment.
378: * @param request
379: * @return
380: * @throws SQLException
381: */
382: private boolean reload(HttpServletRequest request)
383: throws HammurapiWebException {
384: try {
385: SecurityEngine engine = (SecurityEngine) ((Context) request
386: .getAttribute("global")).get("db/SecurityEngine");
387: HttpSession session = request.getSession();
388: User user = (User) session.getAttribute(USER);
389: user = (User) engine.getApplicationUser(
390: user.getLoginName(), User.class);
391:
392: Collection permissions = engine
393: .getApplicationPermission(new ArrayList());
394: Iterator it = permissions.iterator();
395: while (it.hasNext()) {
396: ApplicationPermission ap = (ApplicationPermission) it
397: .next();
398: if (!Boolean.TRUE.equals(user.hasPermission(ap
399: .getClassName(), ap.getActionName()))) {
400: it.remove();
401: }
402: }
403:
404: session.setAttribute(USER, user);
405: session.setAttribute(AUTHORIZATION_PROVIDER,
406: new UserAuthorizationProvider(user, permissions,
407: classPermissions));
408: return getMenuFilter().buildMenu(request);
409: } catch (SQLException e) {
410: throw new HammurapiWebException("Authorization failed: "
411: + e, e);
412: }
413: }
414: }
|