/*
* argun 1.0
* Web 2.0 delivery framework
* Copyright (C) 2007 Hammurapi Group
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* URL: http://www.hammurapi.biz
* e-Mail: support@hammurapi.biz
*/
package biz.hammurapi.web.security;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.codec.binary.Base64;
import biz.hammurapi.authorization.AuthorizationProvider;
import biz.hammurapi.config.Context;
import biz.hammurapi.web.HammurapiWebException;
import biz.hammurapi.web.menu.MenuFilter;
import biz.hammurapi.web.security.sql.ApplicationPermission;
import biz.hammurapi.web.security.sql.SecurityEngine;
/**
* This class handles authentication - it makes sure that each request is authenticated
* @author Pavel Vlasov
* @version $Revision: 1.1 $
*/
public class AuthFilter implements Filter {
public static final String LOGIN_TARGET = "login-target";
public static final String LOGIN_URL_ATTRIBUTE = AuthFilter.class.getName()+":login-url";
public static final String MAX_LOGIN_ATTEMPTS = "max-login-attempts";
public static final String AUTHORIZATION_PROVIDER = AuthorizationProvider.class.getName();
public static final String USER = User.class.getName();
public static final String REMAINING_LOGIN_ATTEMPTS = "remaining-login-attempts";
private static final String DEBUG = "debug";
private String loginUrl;
private String realm;
private int maxAttempts = 6;
private String unauthenticatedPrincipal;
private boolean debug;
private Integer maxAttemptsInteger = new Integer(maxAttempts);
private FilterConfig filterConfig;
/**
* Init method for this filter
*
*/
public void init(final FilterConfig filterConfig) throws ServletException {
filterConfig.getServletContext().log("Initializing authentication filter");
loginUrl=filterConfig.getInitParameter("login-url");
if (loginUrl!=null) {
filterConfig.getServletContext().log("loginUrl="+loginUrl);
}
realm=filterConfig.getInitParameter("realm");
if (realm!=null) {
filterConfig.getServletContext().log("realm="+realm);
}
String maStr = filterConfig.getInitParameter("max-attempts");
if (maStr!=null) {
maxAttempts = Integer.parseInt(maStr);
maxAttemptsInteger = new Integer(maxAttempts);
}
unauthenticatedPrincipal = filterConfig.getInitParameter("unauthenticated-principal");
if (unauthenticatedPrincipal!=null) {
filterConfig.getServletContext().log("unauthenticated principal="+unauthenticatedPrincipal);
}
debug = "yes".equals(filterConfig.getInitParameter(DEBUG));
filterConfig.getServletContext().log("Max login attempts="+maxAttempts);
filterConfig.getServletContext().setAttribute("filter/"+filterConfig.getFilterName(), this);
this.filterConfig = filterConfig;
}
/**
* @return Name of unauthenticated principal (guest user) for this application.
*/
public String getUnauthenticatedPrincipal() {
return unauthenticatedPrincipal;
}
/**
* Checks user in session. If user is present then authorization provider is injected into request.
* If there is no credentials then Guest credentials are used if such user exists and is not blocked.
* If Guest user doesn't exists then control is passed to login url. If login URL is null then basic
* authentication is used.
* @param request The servlet request we are processing
* @param result The servlet response we are creating
* @param chain The filter chain we are processing
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet error occurs
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
HttpSession session = httpRequest.getSession();
session.setAttribute(MAX_LOGIN_ATTEMPTS, maxAttemptsInteger);
if (loginUrl!=null) {
String ctxPath = httpRequest.getContextPath();
if (ctxPath.endsWith("/")) {
ctxPath = ctxPath.substring(0, ctxPath.length()-1);
}
request.setAttribute(LOGIN_URL_ATTRIBUTE, ctxPath+loginUrl);
}
try {
// Get authorization provider.
if (session.getAttribute(AUTHORIZATION_PROVIDER) != null) {
if (debug || "yes".equals(request.getParameter("reloadUser"))) {
reload(httpRequest);
}
chain.doFilter(request, response);
} else {
// Get guest user
if (authorize(httpRequest, null, null)) {
chain.doFilter(request, response);
} else {
if (loginUrl==null) {
// If login page is null then do basic authentication
String auth = httpRequest.getHeader("Authorization");
if (auth == null) {
httpResponse.setHeader("WWW-Authenticate", "BASIC realm=\""+realm+"\"");
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
} else if (auth.toUpperCase().startsWith("BASIC ")) {
String decoded = new String(Base64.decodeBase64(auth.substring(6).getBytes()));
int idx = decoded.indexOf(":");
if (idx != -1 && authorize(httpRequest, decoded.substring(0, idx), decoded.substring(idx+1))) {
session.removeAttribute(REMAINING_LOGIN_ATTEMPTS);
chain.doFilter(request, response);
} else {
Integer attempts = (Integer) session.getAttribute(REMAINING_LOGIN_ATTEMPTS);
if (attempts==null) {
session.setAttribute(REMAINING_LOGIN_ATTEMPTS, maxAttemptsInteger);
} else {
if (attempts.intValue()<=0) {
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Too many failed login attempts");
return;
}
session.setAttribute(REMAINING_LOGIN_ATTEMPTS, new Integer(attempts.intValue()-1));
}
httpResponse.setHeader("WWW-Authenticate", "BASIC realm=\""+realm+"\"");
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
} else {
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "");
}
} else {
// System.out.println(httpRequest.getRequestURI());
if (loginUrl.equals(httpRequest.getRequestURI().substring(httpRequest.getContextPath().length()))) {
chain.doFilter(request, response);
} else {
Integer attempts = (Integer) session.getAttribute(REMAINING_LOGIN_ATTEMPTS);
if (attempts==null) {
session.setAttribute(REMAINING_LOGIN_ATTEMPTS, maxAttemptsInteger);
} else {
if (attempts.intValue()<=0) {
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Too many failed login attempts");
return;
}
session.setAttribute(REMAINING_LOGIN_ATTEMPTS, new Integer(attempts.intValue()-1));
}
// If guest user is null - redirect to login page
String queryString = httpRequest.getQueryString();
if (queryString==null) {
session.setAttribute(LOGIN_TARGET, httpRequest.getRequestURL().toString());
} else {
session.setAttribute(LOGIN_TARGET, httpRequest.getRequestURL().append("?").append(queryString).toString());
}
httpResponse.sendRedirect(httpRequest.getContextPath()+loginUrl);
PrintWriter out = response.getWriter();
out.write("<HTML><HEAD></HEAD><BODY>");
out.write("Redirecting to <a href=\""+httpRequest.getContextPath()+loginUrl+"\">login page</a>");
out.write("</BODY></HTML>");
}
}
}
}
} catch (HammurapiWebException e) {
throw new ServletException("Authorization failed: "+e, e);
}
} else {
chain.doFilter(request, response);
}
}
public void destroy() {
// Nothing to do here.
}
private Map classPermissions = new HashMap();
/**
* Constructs authorization provider for user. Injects user into session.
* @param request
* @param loginName
* @param password
* @return
* @throws SQLException
*/
private boolean authorize(HttpServletRequest request, String loginName, String password) throws HammurapiWebException {
try {
SecurityEngine engine = (SecurityEngine) ((Context) request.getAttribute("global")).get("db/SecurityEngine");
if (loginName==null && getUnauthenticatedPrincipal()==null) {
return false;
}
User user = (User) engine.getApplicationUser(loginName==null ? getUnauthenticatedPrincipal() : loginName, User.class);
if (user==null || user.getIsBlocked()) {
return false;
}
if (loginName!=null && (password==null || !ApplicationUserActions.hashPassword(password).equals(user.getUserPassword()))) {
return false;
}
Collection permissions = engine.getApplicationPermission(new ArrayList());
Iterator it = permissions.iterator();
while (it.hasNext()) {
ApplicationPermission ap = (ApplicationPermission) it.next();
if (!Boolean.TRUE.equals(user.hasPermission(ap.getClassName(), ap.getActionName()))) {
it.remove();
}
}
HttpSession session = request.getSession();
session.setAttribute(USER, user);
session.setAttribute(AUTHORIZATION_PROVIDER, new UserAuthorizationProvider(user, permissions, classPermissions));
return getMenuFilter().buildMenu(request);
} catch (SQLException e) {
throw new HammurapiWebException("Authorization failed: "+e, e);
}
}
private MenuFilter getMenuFilter() {
return (MenuFilter) filterConfig.getServletContext().getAttribute("filter/MenuFilter");
}
/**
* Reloads user and menu at each request. Useful during develpment.
* @param request
* @return
* @throws SQLException
*/
private boolean reload(HttpServletRequest request) throws HammurapiWebException {
try {
SecurityEngine engine = (SecurityEngine) ((Context) request.getAttribute("global")).get("db/SecurityEngine");
HttpSession session = request.getSession();
User user = (User) session.getAttribute(USER);
user = (User) engine.getApplicationUser(user.getLoginName(), User.class);
Collection permissions = engine.getApplicationPermission(new ArrayList());
Iterator it = permissions.iterator();
while (it.hasNext()) {
ApplicationPermission ap = (ApplicationPermission) it.next();
if (!Boolean.TRUE.equals(user.hasPermission(ap.getClassName(), ap.getActionName()))) {
it.remove();
}
}
session.setAttribute(USER, user);
session.setAttribute(AUTHORIZATION_PROVIDER, new UserAuthorizationProvider(user, permissions, classPermissions));
return getMenuFilter().buildMenu(request);
} catch (SQLException e) {
throw new HammurapiWebException("Authorization failed: "+e, e);
}
}
}
|