/*
- * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/filter/SecurityFilter.java,v 1.14 2003/01/06 04:16:32 maxcooper Exp $
- * $Revision: 1.14 $
- * $Date: 2003/01/06 04:16:32 $
+ * $Header: /cvsroot/securityfilter/securityfilter/src/share/org/securityfilter/filter/SecurityFilter.java,v 1.15 2003/01/18 07:18:08 dayash Exp $
+ * $Revision: 1.15 $
+ * $Date: 2003/01/18 07:18:08 $
*
* ====================================================================
* The SecurityFilter Software License, Version 1.1
import org.securityfilter.config.SecurityConstraint;
import org.securityfilter.config.WebResourceCollection;
import org.securityfilter.realm.SecurityRealmInterface;
+import org.apache.catalina.util.Base64;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
* SecurityFilter provides authentication and authorization services.
*
* @author Max Cooper (max@maxcooper.com)
+ * @author Daya Sharma (iamdaya@yahoo.com, billydaya@sbcglobal.net)
* @author Torgeir Veimo (torgeir@pobox.com)
- * @version $Revision: 1.14 $ $Date: 2003/01/06 04:16:32 $
+ * @version $Revision: 1.15 $ $Date: 2003/01/18 07:18:08 $
*/
public class SecurityFilter implements Filter {
public static final String SAVED_REQUEST_URL = SecurityFilter.class.getName() + ".SAVED_REQUEST_URL";
public static final String DEFAULT_CONFIG_FILE = "/WEB-INF/securityfilter-config.xml";
public static final String VALIDATE_KEY = "validate";
public static final String TRUE = "true";
+ public static final String BASIC_WINDOW_SHOWN = "basic_window_shown";
+ public static final String LOGIN_ATTEMPTS = "loginAttempts";
+
+ public static final Base64 base64Helper = new Base64();
protected FilterConfig config;
protected SecurityRealmInterface realm;
protected String defaultPage;
protected URLPatternFactory patternFactory;
protected List patternList;
+ protected String authMethod;
+ protected boolean basic = false;
+ protected String tooManyInCorrectLogins;
+ protected static final String DUMMY_TOKEN = "dummyToken";
protected static final String FORM_USERNAME = "j_username";
protected static final String FORM_PASSWORD = "j_password";
- protected static final String FORM_SUBMIT_URL = "/j_security_check";
+ protected static final String FORM_PATTERN = "formPattern";
+ protected String form_submit_url = "/j_security_check";
/**
* Perform filtering operation, and optionally pass the request down the chain.
* @exception ServletException
*/
public void doFilter(
- ServletRequest request,
- ServletResponse response,
- FilterChain chain
- ) throws IOException, ServletException {
+ ServletRequest request,
+ ServletResponse response,
+ FilterChain chain
+ ) throws IOException, ServletException {
HttpServletRequest hReq = (HttpServletRequest) request;
HttpServletResponse hRes = (HttpServletResponse) response;
URLPattern match = null;
try {
// check if this is a login form submittal
- if (requestURL.endsWith(FORM_SUBMIT_URL)) {
+ if (basicAuthentication(hReq)) {
+ hReq.getSession().removeAttribute(BASIC_WINDOW_SHOWN);
processLogin(wrappedRequest, hRes);
return;
+ } else {
+ if (requestURL.endsWith(form_submit_url)) {
+ processLogin(wrappedRequest, hRes);
+ return;
+ }
}
// only check the request for a security constraint match if it doesn't
// login page and error pages to be viewed even when their URLs would otherwise
// be subject to a security constraint
if (!patternMatcher.match(requestURL, loginPagePattern)
- && !patternMatcher.match(requestURL, loginPagePattern)
- ) {
+ && !patternMatcher.match(requestURL, loginPagePattern)) {
// check if request matches security constraint
match = matchPattern(requestURL, wrappedRequest.getMethod(), patternMatcher);
}
Collection roles = authConstraint.getRoles();
Principal principal = wrappedRequest.getUserPrincipal();
// if roles is empty, access will be blocked no matter who the user is, so skip the login
- if (!roles.isEmpty() && principal == null) {
+ if (!roles.isEmpty() && principal == null && hReq.getSession().getAttribute(DUMMY_TOKEN) == null) {
// user needs to be authenticated
showLogin(hReq, hRes);
return;
chain.doFilter(request, response);
}
+ private boolean basicAuthentication(HttpServletRequest hReq) {
+ return authMethod.equalsIgnoreCase("basic") && hReq.getSession().getAttribute(BASIC_WINDOW_SHOWN) != null
+ && hReq.getHeader("Authorization") != null;
+ }
+
/**
* Initialize the SecurityFilter.
*
if (configFile == null) {
configFile = DEFAULT_CONFIG_FILE;
}
+ form_submit_url = config.getInitParameter(FORM_PATTERN);
URL configURL = config.getServletContext().getResource(configFile);
String validate = config.getInitParameter(VALIDATE_KEY);
SecurityConfig securityConfig = new SecurityConfig(TRUE.equalsIgnoreCase(validate));
loginPagePattern = patternFactory.createURLPattern(loginPage, null, null, 0);
errorPage = securityConfig.getErrorPage();
errorPagePattern = patternFactory.createURLPattern(errorPage, null, null, 0);
+ authMethod = securityConfig.getAuthMethod();
+ // todo: support DIGEST and CERT authentication schemes.
+ if (authMethod.equalsIgnoreCase(HttpServletRequest.BASIC_AUTH))
+ basic = true;
// create pattern list
patternList = new ArrayList();
WebResourceCollection resourceCollection = (WebResourceCollection) rIter.next();
for (Iterator pIter = resourceCollection.getURLPatterns().iterator(); pIter.hasNext();) {
URLPattern pattern = patternFactory.createURLPattern(
- (String) pIter.next(),
- constraint,
- resourceCollection,
- order++
+ (String) pIter.next(),
+ constraint,
+ resourceCollection,
+ order++
);
patternList.add(pattern);
}
* @exception ServletException
*/
protected void showLogin(
- HttpServletRequest request,
- HttpServletResponse response
- ) throws IOException, ServletException {
+ HttpServletRequest request,
+ HttpServletResponse response
+ ) throws IOException, ServletException {
// save this request
saveRequestInformation(request);
// redirect to login page
- response.sendRedirect(response.encodeRedirectURL(request.getContextPath() + loginPage));
+ request.getSession().setAttribute(BASIC_WINDOW_SHOWN, "shown");
+ int loginAttempts = 1;
+ if (request.getSession().getAttribute(LOGIN_ATTEMPTS) != null) {
+ loginAttempts = ((Integer) request.getSession().getAttribute(LOGIN_ATTEMPTS)).intValue();
+ loginAttempts += 1;
+ }
+ // todo: we can put some useful message here, perhaps a internationlizable format of message.
+ tooManyInCorrectLogins = "Sorry you are having problems logging in, please try again";
+ String loginAttemptMessage = "Login attempt number " + loginAttempts;
+ String logo;
+ if (basic) {
+ if (loginAttempts <= 3) {
+ String realm = String.valueOf(Math.random());
+ if (loginAttempts < 2) {
+ logo = "Basic Auth with Security Filter";
+ } else {
+ logo = loginAttemptMessage;
+ }
+ String blankLine = " ";
+ logo = blankLine + blankLine + blankLine + logo + blankLine + blankLine;
+ System.err.println("response.setHeader \"WWW-Authenticate\", \"BASIC realm=\"" + logo + realm + "\"");
+ response.setHeader("WWW-Authenticate", "BASIC realm=\"" + realm + logo + "\"");
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ request.getSession().setAttribute(LOGIN_ATTEMPTS, new Integer(loginAttempts));
+ } else {
+ request.getSession().removeAttribute(LOGIN_ATTEMPTS);
+ response.sendError(HttpServletResponse.SC_UNAUTHORIZED, tooManyInCorrectLogins);
+ }
+ } else {
+ response.sendRedirect(request.getContextPath() + loginPage);
+ return;
+ }
}
/**
* @exception ServletException
*/
protected void processLogin(
- SecurityRequestWrapper request,
- HttpServletResponse response
- ) throws IOException, ServletException {
+ SecurityRequestWrapper request,
+ HttpServletResponse response
+ ) throws IOException, ServletException {
String username = request.getParameter(FORM_USERNAME);
String password = request.getParameter(FORM_PASSWORD);
+ if (basic && username == null && password == null) {
+ username = parseUsername(request.getHeader("Authorization"));
+ password = parsePassword(request.getHeader("Authorization"));
+ }
Principal principal = realm.authenticate(username, password);
if (principal != null) {
// login successful
// invalidate old session if the user was already authenticated
// NOTE: we may want to check if the user re-authenticated as the same user, currently
// the session will be invalidated even if the user authenticates as the same user.
- if (request.getUserPrincipal() != null) {
- request.getSession().invalidate();
- }
request.setUserPrincipal(principal);
String continueToURL = getContinueToURL(request);
+ request.getSession().setAttribute(DUMMY_TOKEN, DUMMY_TOKEN);
+ // remove the saved request from the session.
+ // This is the url that the user was initially accessing before being prompted for login.
+ removeSavedRequest(request.getSession());
response.sendRedirect(response.encodeRedirectURL(continueToURL));
} else {
- // login failed, set response status and forward to error page
- response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
- request.getRequestDispatcher(errorPage).forward(request, response);
+ // login failed
+ if (!basic) {
+ // set response status and forward to error page
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ request.getRequestDispatcher(errorPage).forward(request, response);
+ } else {
+ // show the basic authentication window again.
+ showLogin(request.getCurrentRequest(), response);
+ }
}
}
+ private void removeSavedRequest(HttpSession session) {
+ session.removeAttribute(SecurityFilter.SAVED_REQUEST_URL);
+ session.removeAttribute(SecurityFilter.SAVED_REQUEST);
+ }
+
/**
* Get the URL to continue to after successful login. This may be the SAVED_REQUEST_URL if the authorization
* sequence was initiated by the filter, or the default URL (as specified in the config file) if a login
String savedURL = (String) session.getAttribute(SecurityFilter.SAVED_REQUEST_URL);
if (savedURL != null && savedURL.equals(getSaveableURL(request))) {
// this is a request for the request that caused the login,
- // return the SavedRequest and remove it from the session
- SavedRequest savedRequest = (SavedRequest) session.getAttribute(SecurityFilter.SAVED_REQUEST);
- session.removeAttribute(SecurityFilter.SAVED_REQUEST_URL);
- session.removeAttribute(SecurityFilter.SAVED_REQUEST);
- return savedRequest;
+ // return the SavedRequest
+ return (SavedRequest) session.getAttribute(SecurityFilter.SAVED_REQUEST);
} else {
return null;
}
* @param request the request to construct a saveable URL for
*/
private String getSaveableURL(HttpServletRequest request) {
- StringBuffer saveableURL = request.getRequestURL();
+ String url = null;
+ StringBuffer saveableURL = null;
+ String protocol = request.getProtocol();
+ try {
+ saveableURL = request.getRequestURL();
+ } catch (NoSuchMethodError e) {
+ // this is done to support app servers like orion 1.5.2
+ // which have not implemented the servlet 2.3 specification but have implemented the final draft of 2.3 spec.
+ if (protocol.equals("HTTP/1.1")) { // todo: provide support for ftp, webdav protocol among others.
+ protocol = "http://";
+ if (request.isSecure())
+ protocol = "https://";
+ }
+ url = protocol + request.getServerName() + ":" + request.getServerPort() + request.getRequestURI();
+ }
+ if (saveableURL == null) {
+ saveableURL = new StringBuffer(url);
+ } else {
+ // since HTTP is the same regardless of whether it runs on TCP or on SSL/TCP
+ if (protocol.equals("HTTP/1.1") && request.isSecure() && saveableURL.toString().startsWith("http://")) {
+ saveableURL.replace(0, 4, "https"); // todo: this needs to be tested extensively.
+ }
+ }
// add the query string, if any
String queryString = request.getQueryString();
if (queryString != null) {
}
return saveableURL.toString();
}
+
+ private String parseUsername(String authorization) {
+
+ String unencoded = getdecodedString(authorization);
+ if (unencoded == null) return null;
+ int colon = unencoded.indexOf(':');
+ if (colon < 0) return (null);
+ return unencoded.substring(0, colon).trim();
+ }
+
+ private String getdecodedString(String authorization) {
+ if (authorization == null)
+ return (null);
+ if (!authorization.toLowerCase().startsWith("basic "))
+ return (null);
+ authorization = authorization.substring(6).trim();
+ // Decode and parse the authorization credentials
+ return new String(base64Helper.decode(authorization.getBytes()));
+ }
+
+ private String parsePassword(String authorization) {
+ String unencoded = getdecodedString(authorization);
+ if (unencoded == null) return null;
+ int colon = unencoded.indexOf(':');
+ if (colon < 0) return (null);
+ return unencoded.substring(colon + 1).trim();
+ }
}
// ------------------------------------------------------------------------